Merge branch 'dev' into 'main'

Dev

See merge request ql/ql_billing_system_backend!1
This commit is contained in:
西街长安 2024-12-06 03:39:18 +00:00
commit a66ec83311
98 changed files with 7687 additions and 71 deletions

63
.gitattributes vendored Normal file
View File

@ -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

363
.gitignore vendored Normal file
View File

@ -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

37
APIDefine.md Normal file
View File

@ -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 | 操作日志查询 |

25
Apimanager_backend.sln Normal file
View File

@ -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

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0" />
<PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.MariaDB" Version="1.0.1" />
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
@Apimanager_backend_HostAddress = http://localhost:5292
GET {{Apimanager_backend_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -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<User,UserInfoDto>();
CreateMap<CreateUserDto, User>()
.ForMember(dest => dest.PassHash, opt => opt.MapFrom(src => src.Password));
CreateMap<Api, ApiInfoDto>();
CreateMap<CreateApiInfo, Api>();
CreateMap<Apipackage, PackageInfoDto>();
CreateMap<AddPackageDto, Apipackage>();
}
}
}

View File

@ -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<IUserService, UserService>();
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IAdminService, AdminService>();
services.AddScoped<IApiService, ApiService>();
services.AddScoped<IPackageService, PackageService>();
services.AddScoped<ISystemConfigService, SystemConfigService>();
services.AddSingleton<ITokenService, TokenService>();
services.AddSingleton<IRefreshTokenService, RefreshTokenService>();
services.AddSingleton<IEmailService, EmailService>();
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<JwtBearerChallengeContext, Task>(JwtTokenErrorEventFunc),
OnForbidden = new Func<ForbiddenContext, Task>(JwtPermissionEventFunc)
};
});
//redis配置
services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(configuration["Redis:ConnectionString"]));
return services;
}
/// <summary>
/// token无效事件处理函数
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async static Task JwtTokenErrorEventFunc(JwtBearerChallengeContext context)
{
context.Response.ContentType = "application/json";
var res = new ResponseBase<object?>(
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<object?>(
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;
}
}
}

View File

@ -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<ActionResult<ResponseBase<List<UserInfoDto>>>> UserList(int pageIndex,int pageSize,bool desc)
{
var users = await adminService.GetUsersAsync(pageIndex,pageSize,desc);
var res = new ResponseBase<List<UserInfoDto>>(
code:1000,
message:"Success",
data:users
);
return Ok(res);
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<UserInfoDto?>>> UserInfo(int userId)
{
var userInfo = await userService.GetUserAsync(userId);
var res = new ResponseBase<UserInfoDto?>(
code: 1000,
message: "Success",
data: userInfo
);
return Ok(res);
}
#endregion
#region
[HttpDelete]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> DeleteUser(int userId)
{
await adminService.DeleteUserAsync(userId);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data: null
);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<UserInfoDto?>>> AddUser([FromBody]CreateUserDto dto)
{
var userInfo = await adminService.CreateUserAsync(dto);
var res = new ResponseBase<UserInfoDto?>(
code:1000,
message:"Success",
data: userInfo
);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> Ban(int userId)
{
await adminService.BanUserAsync(userId);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data: null
);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> UnBan(int userId)
{
await adminService.UnbanUserAsync(userId);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data:null
);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<UserInfoDto?>>> UpdateUser([FromQuery]int userId,[FromBody]AdminUpdateUserDto dto)
{
try
{
var userInfo = await adminService.UpdateUserAsync(userId, dto);
var res = new ResponseBase<UserInfoDto?>(
code: 1000,
message: "Success",
data: userInfo
);
return Ok(res);
}
catch(BaseException e)
{
var res = new ResponseBase<UserInfoDto?>(
code: e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<int>>> UserCount()
{
int count = await adminService.UserCountAsync();
var res = new ResponseBase<int>(
code:1000,
message:"Success",
data:count
);
return Ok(res);
}
#endregion
}
}

View File

@ -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<ApisController> logger;
public ApisController(IApiService apiService,ILogger<ApisController> logger)
{
this.apiService = apiService;
this.logger = logger;
}
#region API信息
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<ApiInfoDto>>> ApiInfo(int apiId)
{
try
{
var apiInfo = await apiService.GetApiInfoAsync(apiId);
var res = new ResponseBase<ApiInfoDto>(
code: 1000,
message: "Success",
data: apiInfo
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<ApiInfoDto>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
#endregion
#region API列表
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<List<ApiInfoDto>>>> ApiList(int pageIndex,int pageSize,bool desc)
{
var list = await apiService.GetApisAsync(pageIndex, pageSize, desc);
var res = new ResponseBase<List<ApiInfoDto>>(
code:1000,
message:"Success",
data:list
);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<ApiInfoDto>>> AddApi([FromBody]CreateApiInfo createApiInfo)
{
var apiInfo = await apiService.AddApiAsync(createApiInfo);
var res = new ResponseBase<ApiInfoDto>(
code:1000,
message:"Success",
data: apiInfo
);
return Ok(res);
}
#endregion
#region
[HttpDelete]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> DeleteApi(int apiId)
{
try
{
await apiService.DeleteApiAsync(apiId);
var res = new ResponseBase<object?>(
code: 1000,
message: "Success",
data: null
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<object?>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
#endregion
#region api信息
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<ApiInfoDto>>> UpdateApi(int apiId,UpdateApiDto updateApiDto)
{
try
{
//更新
var apiInfo = await apiService.UpdateApiAsync(apiId, updateApiDto);
var res = new ResponseBase<ApiInfoDto>(
code: 1000,
message: "Success",
data: apiInfo
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<ApiInfoDto>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
#endregion
#region APi
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> BanApi(int userId)
{
try
{
await apiService.OffApiAsync(userId);
var res = new ResponseBase<object?>(
code: 1000,
message: "Success",
data: null
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<object?>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
#endregion
#region API
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> UnBanApi(int userId)
{
try
{
await apiService.OnApiAsync(userId);
var res = new ResponseBase<object?>(
code: 1000,
message: "Success",
data: null
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<object?>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
#endregion
}
}

View File

@ -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;
}
/// <summary>
/// 用户登录控制器
/// </summary>
/// <param name="dto">登录信息</param>
/// <returns>通用返回信息格式</returns>
[HttpPost]
public async Task<ActionResult<ResponseBase<UserInfoDto>>> 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<LoginResponseDto>(
code: 2000,
message: "Login successful",
data: new LoginResponseDto
{
UserInfo = user,
Token = token,
RefreshToken = refreshToken
}
);
return Ok(responseInfo);
}
/// <summary>
/// 令牌刷新
/// </summary>
/// <param name="dto">传入用户令牌</param>
/// <returns>返回新令牌</returns>
[HttpPost]
public async Task<ActionResult<ResponseBase<RefreshResponseDto?>>> Refresh([FromBody]RefreshResponseDto dto)
{
var IsRefreshToken = await refreshTokenService.ValidateRefreshTokenAsync(dto.UserId.ToString(),dto.RefreshToken);
//刷新令牌无效
if (!IsRefreshToken)
{
var ret = new ResponseBase<RefreshResponseDto?>(
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<RefreshResponseDto?>(
code: 1000,
message: "Success",
data: new RefreshResponseDto
{
UserId = dto.UserId,
Token = token,
RefreshToken = dto.RefreshToken
}
);
return Ok(result);
}
/// <summary>
/// 用户注册
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
[HttpPost]
public async Task<ActionResult<ResponseBase<UserInfoDto?>>> Register(RegisterRequestDto requestDto)
{
var isUsernameExist = await userService.IsUsernameExist(requestDto.Username);
if (isUsernameExist)
{
var errorRes = new ResponseBase<UserInfoDto?>(
code: 2003,
message: "用户名已存在",
data: null
);
return StatusCode(409, errorRes);
}
try
{
var userInfo = await authService.RegisterAsync(requestDto);
var res = new ResponseBase<UserInfoDto?>(
code: 1000,
message: "Success",
data: userInfo
);
return Ok(res);
}
catch (BaseException e)
{
var res = new ResponseBase<UserInfoDto?>(
code: e.code,
message: e.message,
data: null
);
return StatusCode(500, res);
}
}
/// <summary>
/// 发送邮箱校验码
/// </summary>
/// <param name="registerRequestDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<ResponseBase<object?>>> SendValidateCode([FromQuery]string email)
{
//检测邮箱是否被使用
var emailIsUse = await userService.IsEmailExist(email);
if (emailIsUse)
{
var errorRes = new ResponseBase<object?>(
code:2005,
message: "邮箱已存在",
data:null
);
return StatusCode(409,errorRes);
}
//发送注册验证码
await authService.SendRegisterCodeAsync(email);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data: null
);
return Ok(res);
}
[HttpDelete]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<object?>>> Logout()
{
var userId = User.Claims.First(x => x.ValueType == "userId").Value;
await refreshTokenService.DeleterRefreshTokenAsync(userId);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data: null
);
return Ok(res);
}
}
}

View File

@ -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<PackageController> logger;
public PackageController(IPackageService packageService,ILogger<PackageController> logger)
{
this.packageService = packageService;
this.logger = logger;
}
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<PackageInfoDto>>> AddPackage(AddPackageDto addPackageDto)
{
var packageInfo = await packageService.AddPackageAsync(addPackageDto);
var res = new ResponseBase<PackageInfoDto>(packageInfo);
return Ok(res);
}
#endregion
#region
[HttpDelete]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<object?>>> DeletePackage(int packageId)
{
try
{
await packageService.DeletePackageAsync(packageId);
var res = new ResponseBase<object?>(null);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<object?>(
code:e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<PackageInfoDto?>>> UpdatePackage(int packageId, UpdatePackageDto updatePackageDto)
{
try
{
var packageInfo = await packageService.UpdatePackageAsync(packageId, updatePackageDto);
var res = new ResponseBase<PackageInfoDto?>(packageInfo);
return Ok(res);
}
catch (BaseException e)
{
var res = new ResponseBase<PackageInfoDto?>(
code:e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<PackageInfoDto?>>> GetPackageInfo(int packageId)
{
try
{
var packageInfo = await packageService.PackageInfoByIdAsync(packageId);
var res = new ResponseBase<PackageInfoDto?>(packageInfo);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<PackageInfoDto?>(
code:e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<List<PackageInfoDto>>>> GetPackageList(int pageIndex,int pageSize,bool desc)
{
var packageList = await packageService.GetAllPackagesAsync(pageIndex,pageSize,desc);
var res = new ResponseBase<List<PackageInfoDto>>(packageList);
return Ok(res);
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<List<UserPackage>>>> 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<UserPackage>>(list);
return Ok(res);
}
#endregion
#region
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<List<UserPackage>>>> GetUserPackageListAdmin(int userId,int pageIndex, int pageSize, bool desc)
{
var list = await packageService.GetUserPackagesAsync(userId);
var res = new ResponseBase<List<UserPackage>>(list);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<UserPackage>>> 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>(userPackage);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<UserPackage>>> 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>(userPackage);
return Ok(res);
}
#endregion
#region
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<UserPackage?>>> 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>(userPackage);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<UserPackage?>(
code:e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
#endregion
}
}

View File

@ -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<SystemConfigController> logger;
public SystemConfigController(ISystemConfigService systemConfigService, ILogger<SystemConfigController> logger)
{
this.systemConfigService = systemConfigService;
this.logger = logger;
}
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<ResponseBase<SystemConfig?>>> UpdateSystemConfig([FromBody]UpdateSystemConfigDto updateSystemConfigDto)
{
try
{
var configInfo = await systemConfigService.UpdateSystemConfig(updateSystemConfigDto.ConfigName, updateSystemConfigDto.ConfigBody);
var res = new ResponseBase<SystemConfig?>(configInfo);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<SystemConfig?>(
code:e.code,
message:e.message,
data:null
);
return NotFound(res);
}
}
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<SystemConfig?>>> GetSystemConfig(string configName)
{
try
{
var configInfo = await systemConfigService.GetSystemConfig(configName);
var res = new ResponseBase<SystemConfig?>(configInfo);
return Ok(res);
}
catch (BaseException e)
{
var res = new ResponseBase<SystemConfig?>(
code: e.code,
message: e.message,
data: null
);
return NotFound(res);
}
}
}
}

View File

@ -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;
}
/// <summary>
/// 获取用户个人信息
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<UserInfoDto>>> UserInfo()
{
var userId = User.Claims.First(x => x.Type == "userId").Value;
var userInfo = await userService.GetUserAsync(int.Parse(userId));
var res = new ResponseBase<UserInfoDto>(
code:1000,
message:"Success",
data:userInfo
);
return Ok(res);
}
/// <summary>
/// 重置用户密码
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<ResponseBase<object?>>> Resetpassword([FromBody]ResetPasswordDto dto)
{
try
{
await userService.ResetPasswordAsync(dto.Email, dto.Code, dto.NewPassword);
var res = new ResponseBase<object?>(
code:1000,
message:"Success",
data: null
);
return Ok(res);
}catch(BaseException e)
{
var res = new ResponseBase<object?>(
code:e.code,
message:e.message,
data:null
);
return StatusCode(400,res);
}
}
[HttpPost]
public async Task<ActionResult<ResponseBase<object?>>> SendResetEmail([FromQuery]string email)
{
await userService.SendResetPasswordEmailAsync(email);
var res = new ResponseBase<object?>(
code: 1000,
message: "Success",
data: null
);
return Ok(res);
}
[HttpPost]
[Authorize(Roles = "User")]
public async Task<ActionResult<ResponseBase<UserInfoDto?>>> 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<object?>(
code:1000,
message:"Success",
data:userInfo
);
return Ok(res);
}
}
}

View File

@ -0,0 +1,26 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class ApiCallLogConfig : IEntityTypeConfiguration<ApiCallLog>
{
public void Configure(EntityTypeBuilder<ApiCallLog> 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);
}
}
}

View File

@ -0,0 +1,23 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class ApiConfig : IEntityTypeConfiguration<Api>
{
public void Configure(EntityTypeBuilder<Api> 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);
}
}
}

View File

@ -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<Api> Apis { get; set; }
//用户表
public DbSet<User> Users { get; set; }
//API调用日志
public DbSet<ApiCallLog> CallLogs { get; set; }
//套餐表
public DbSet<Apipackage> Apipackages { get; set; }
//操作日志表
public DbSet<OperationLog> OperationLogs { get; set; }
//订单表
public DbSet<Order> Orders { get; set; }
//用户已订购套餐表
public DbSet<UserPackage> UserPackages { get; set; }
//用户角色表
public DbSet<UserRole> UserRoles { get; set; }
//日志表
public DbSet<Log> Logs { get; set; }
//系统配置表
public DbSet<SystemConfig> SystemConfigs { get; set; }
public ApiContext(DbContextOptions<ApiContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApiContext).Assembly);
// 配置全局查询筛选器
modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDelete);
modelBuilder.Entity<Api>().HasQueryFilter(a => !a.IsDelete);
modelBuilder.Entity<Apipackage>().HasQueryFilter(x => x.IsDeleted);
//配置日志表
modelBuilder.Entity<Log>().HasKey(x => x.Id);
modelBuilder.Entity<Log>()
.Property(x => x.Id)
.ValueGeneratedOnAdd();
}
}
}

View File

@ -0,0 +1,19 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class ApiRequestExampleConfig : IEntityTypeConfiguration<ApiRequestExample>
{
public void Configure(EntityTypeBuilder<ApiRequestExample> builder)
{
//主键
builder.HasKey(x => x.Id);
//外键
builder.HasOne(x => x.Api)
.WithMany(u => u.ApiRequestExamples)
.HasForeignKey(x => x.ApiId);
}
}
}

View File

@ -0,0 +1,22 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class OperationLogConfig : IEntityTypeConfiguration<OperationLog>
{
public void Configure(EntityTypeBuilder<OperationLog> 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);
}
}
}

View File

@ -0,0 +1,28 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class OrderConfig : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> 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);
}
}
}

View File

@ -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<SystemConfig>
{
public void Configure(EntityTypeBuilder<SystemConfig> 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 })
)
);
}
}
}

View File

@ -0,0 +1,26 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class UserConfig : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> 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")
);
}
}
}

View File

@ -0,0 +1,25 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class UserPackageConfig : IEntityTypeConfiguration<UserPackage>
{
public void Configure(EntityTypeBuilder<UserPackage> 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);
}
}
}

View File

@ -0,0 +1,21 @@
using Apimanager_backend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Apimanager_backend.Data
{
public class UserRoleConfig : IEntityTypeConfiguration<UserRole>
{
public void Configure(EntityTypeBuilder<UserRole> 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);
}
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,8 @@
namespace Apimanager_backend.Dtos
{
public class AdminUpdateUserDto
{
public string? Password { get; set; }
public decimal? Balance { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,26 @@
namespace Apimanager_backend.Dtos
{
/// <summary>
/// 响应基类,构造最后的返回结果
/// </summary>
/// <typeparam name="T">返回的具体数据类型</typeparam>
public class ResponseBase<T>
{
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() { }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,8 @@
namespace Apimanager_backend.Dtos
{
public class UpdateSystemConfigDto
{
public string ConfigName { get; set; }
public string ConfigBody { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,16 @@
using Apimanager_backend.Models;
namespace Apimanager_backend.Dtos
{
public class UserInfoBaseDto
{
public UserInfoDto UserInfo { get; set; }
public List<UserRole> Roles { get; set; }
public UserInfoBaseDto(UserInfoDto userInfo, List<UserRole> roles)
{
UserInfo = userInfo;
Roles = roles;
}
public UserInfoBaseDto() { }
}
}

View File

@ -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<UserRole> Roles { get; set; }
public bool IsBan { get; set; }
public decimal Balance { get; set; }
public DateTime Created { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}
}

View File

@ -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<object?>(
code:exception.code,
message: exception.message,
data:null
);
int httpCode = StatusCodeHelper.GetHttpStatusCode(exception.code);
context.Result = new JsonResult(res) {
StatusCode = httpCode
};
context.ExceptionHandled = true;
}
}
}
}

View File

@ -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<object?>
{
Code = 1001, // 统一的错误码
Message = errors[0],
Data = null
};
// 设置错误响应和 HTTP 状态码
context.Result = new BadRequestObjectResult(response);
}
}
}
}

View File

@ -0,0 +1,378 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<int>("Role")
.HasColumnType("int");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("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
}
}
}

View File

@ -0,0 +1,295 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apimanager_backend.Migrations
{
/// <inheritdoc />
public partial class InitDatabase : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Apipackages",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CallLimit = table.Column<int>(type: "int", nullable: false),
Price = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
ExpiryDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedAt = table.Column<DateTime>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Username = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
PassHash = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Role = table.Column<int>(type: "int", nullable: false),
IsBan = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsDelete = table.Column<bool>(type: "tinyint(1)", nullable: false),
Balance = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
CreatedAt = table.Column<DateTime>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Endpoint = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Method = table.Column<int>(type: "int", nullable: false),
PackageId = table.Column<int>(type: "int", nullable: true),
IsThirdParty = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsDelete = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
Operation = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TargetType = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TargetId = table.Column<int>(type: "int", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
IpAddress = table.Column<string>(type: "varchar(45)", maxLength: 45, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
UserAgent = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
OrderNumber = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ThirdPartyOrderId = table.Column<string>(type: "varchar(255)", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Amount = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
OrderType = table.Column<int>(type: "int", nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Description = table.Column<string>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
PackageId = table.Column<int>(type: "int", nullable: false),
RemainingCalls = table.Column<int>(type: "int", nullable: false),
PurchasedAt = table.Column<DateTime>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
ApiId = table.Column<int>(type: "int", nullable: false),
CallTime = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CallResult = table.Column<int>(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);
}
/// <inheritdoc />
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");
}
}
}

View File

@ -0,0 +1,408 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserPackages");
});
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Role")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("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
}
}
}

View File

@ -0,0 +1,60 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apimanager_backend.Migrations
{
/// <inheritdoc />
public partial class adduserroletable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Role",
table: "Users");
migrationBuilder.CreateTable(
name: "UserRoles",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
Role = table.Column<string>(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");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserRoles");
migrationBuilder.AddColumn<int>(
name: "Role",
table: "Users",
type: "int",
nullable: false,
defaultValue: 0);
}
}
}

View File

@ -0,0 +1,442 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.Log", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Exception")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LogLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MessageTemplate")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Properties")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("Logs");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserPackages");
});
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Role")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("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
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apimanager_backend.Migrations
{
/// <inheritdoc />
public partial class updatelogtable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -0,0 +1,493 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<string>("Request")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Response")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("ResponseType")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ApiId");
b.ToTable("ApiRequestExample");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("OneMinuteLimit")
.HasColumnType("int");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.Log", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Exception")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LogLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MessageTemplate")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Properties")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("Logs");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ApiKey")
.HasColumnType("longtext");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserPackages");
});
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Role")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("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
}
}
}

View File

@ -0,0 +1,86 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apimanager_backend.Migrations
{
/// <inheritdoc />
public partial class addapirequestExampleTable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ApiKey",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "Description",
table: "Apis",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<int>(
name: "OneMinuteLimit",
table: "Apipackages",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "ApiRequestExample",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ApiId = table.Column<int>(type: "int", nullable: false),
ResponseType = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Request = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Response = table.Column<string>(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");
}
/// <inheritdoc />
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");
}
}
}

View File

@ -0,0 +1,572 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<string>("Request")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Response")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("ResponseType")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ApiId");
b.ToTable("ApiRequestExample");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("IsDeleted")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("OneMinuteLimit")
.HasColumnType("int");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.Log", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Exception")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LogLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MessageTemplate")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Properties")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("Logs");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ConfigBody")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ApiKey")
.HasColumnType("longtext");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserPackages");
});
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Role")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("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
}
}
}

View File

@ -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
{
/// <inheritdoc />
public partial class addconfigTable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ExpiryDate",
table: "Apipackages");
migrationBuilder.AddColumn<DateTime>(
name: "ExpiryDate",
table: "UserPackages",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<bool>(
name: "IsDeleted",
table: "Apipackages",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "SystemConfigs",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ConfigName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ConfigBody = table.Column<string>(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" });
}
/// <inheritdoc />
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<DateTime>(
name: "ExpiryDate",
table: "Apipackages",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
}
}

View File

@ -0,0 +1,569 @@
// <auto-generated />
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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsThirdParty")
.HasColumnType("tinyint(1)");
b.Property<int>("Method")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<int?>("PackageId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.ToTable("Apis");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<int>("CallResult")
.HasColumnType("int");
b.Property<DateTime>("CallTime")
.HasColumnType("datetime(6)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ApiId");
b.HasIndex("UserId");
b.ToTable("CallLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ApiId")
.HasColumnType("int");
b.Property<string>("Request")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Response")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("ResponseType")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ApiId");
b.ToTable("ApiRequestExample");
});
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CallLimit")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("IsDeleted")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("OneMinuteLimit")
.HasColumnType("int");
b.Property<decimal>("Price")
.HasColumnType("decimal(65,30)");
b.HasKey("Id");
b.ToTable("Apipackages");
});
modelBuilder.Entity("Apimanager_backend.Models.Log", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Exception")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LogLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MessageTemplate")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Properties")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("Logs");
});
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(45)
.HasColumnType("varchar(45)");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<int>("TargetId")
.HasColumnType("int");
b.Property<string>("TargetType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("UserAgent")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("OperationLogs");
});
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("OrderNumber")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("OrderType")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("ThirdPartyOrderId")
.HasColumnType("varchar(255)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ConfigBody")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ApiKey")
.HasColumnType("longtext");
b.Property<decimal>("Balance")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("IsBan")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsDelete")
.HasColumnType("tinyint(1)");
b.Property<string>("PassHash")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime(6)");
b.Property<int>("PackageId")
.HasColumnType("int");
b.Property<DateTime>("PurchasedAt")
.HasColumnType("datetime(6)");
b.Property<int>("RemainingCalls")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserPackages");
});
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Role")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("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
}
}
}

View File

@ -0,0 +1,64 @@
using Apimanager_backend.Data;
using System.ComponentModel.DataAnnotations;
namespace Apimanager_backend.Models
{
public class Api
{
/// <summary>
/// 主键,自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// API名称
/// </summary>
[MaxLength(200)]
[Required]
public string Name { get; set; } // varchar(20)
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// API地址
/// </summary>
[Required]
public string Endpoint { get; set; } // varchar(255)
/// <summary>
/// 调用方法
/// </summary>
public ApiMethod Method { get; set; } // enum('GET','POST','PUT', 'DELETE')
/// <summary>
/// 套餐Id默认为空免费无限制
/// </summary>
public int? PackageId { get; set; } // int? 使其可为null
/// <summary>
/// 是否第三方API
/// </summary>
public bool IsThirdParty { get; set; } // boolean
/// <summary>
/// 是否启用
/// </summary>
public bool IsActive { get; set; } // boolean
/// <summary>
/// 是否删除
/// </summary>
public bool IsDelete { get; set; } // boolean
/// <summary>
/// 创建时间,默认当前时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp
//导航属性
public Apipackage? Package { get; set; }
public ICollection<ApiCallLog> ApiCalls { get; set; }
public ICollection<ApiRequestExample> ApiRequestExamples { get; set; }
}
}

View File

@ -0,0 +1,35 @@
namespace Apimanager_backend.Models
{
public class ApiCallLog
{
/// <summary>
/// 主键,自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// 外键用户ID
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 外键API ID
/// </summary>
public int ApiId { get; set; }
/// <summary>
/// 调用时间,默认当前时间
/// </summary>
public DateTime CallTime { get; set; } = DateTime.UtcNow; // Timestamp
/// <summary>
/// 调用结果状态码
/// </summary>
public int CallResult { get; set; } // 调用结果状态码
//导航属性
public User? User { get; set; }
public Api? Api { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace Apimanager_backend.Models
{
public enum ApiMethod
{
GET = 0,
POST = 1,
PUT = 2,
DELETE = 3
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace Apimanager_backend.Models
{
public class Apipackage
{
/// <summary>
/// 主键,自增
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// 套餐名称
/// </summary>
[MaxLength(20)]
public string Name { get; set; } // varchar(20)
/// <summary>
/// 最大调用次数
/// </summary>
public int CallLimit { get; set; } // int
/// <summary>
/// 价格
/// </summary>
public decimal Price { get; set; } // decimal(10,2)
/// <summary>
/// 每分钟调用次数限制
/// </summary>
public int OneMinuteLimit { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp
/// <summary>
/// 是否删除
/// </summary>
public bool IsDeleted { get; set; } = false;
//导航属性
public ICollection<Api> Apis { get; set; }
public ICollection<UserPackage> Packages { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,61 @@
using Microsoft.AspNetCore.Antiforgery;
using System.ComponentModel.DataAnnotations;
namespace Apimanager_backend.Models
{
public class OperationLog
{
/// <summary>
/// 主键,自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// 操作人ID操作者的用户ID通常是管理员
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 操作类型,描述操作的具体内容
/// </summary>
[Required]
[MaxLength(20)]
public string Operation { get; set; } // varchar(20)
/// <summary>
/// 目标类型,操作的对象类型
/// </summary>
[Required]
[MaxLength(50)]
public string TargetType { get; set; } // varchar(50)
/// <summary>
/// 目标对象ID操作对象的具体ID
/// </summary>
public int TargetId { get; set; }
/// <summary>
/// 操作时间,操作发生时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp
/// <summary>
/// 操作来源IP
/// </summary>
[MaxLength(45)]
public string IpAddress { get; set; } // varchar(45)
/// <summary>
/// 操作设备信息
/// </summary>
public string UserAgent { get; set; } // varchar(255)
/// <summary>
/// 操作描述,可选的详细说明,解释操作原因等
/// </summary>
public string Description { get; set; } // varchar(255)
//导航属性
public User? User { get; set; }
}
}

View File

@ -0,0 +1,62 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace Apimanager_backend.Models
{
public class Order
{
/// <summary>
/// 主键,自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// 外键用户ID
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 订单号,唯一
/// </summary>
[Required]
public string OrderNumber { get; set; } // varchar(50)
/// <summary>
/// 第三方系统订单编号
/// </summary>
public string? ThirdPartyOrderId { get; set; } // varchar(100)
/// <summary>
/// 订单金额
/// </summary>
public decimal Amount { get; set; } // decimal(10, 2)
/// <summary>
/// 订单类型
/// </summary>
public OrderType OrderType { get; set; } // enum('Recharge','Purchase','Refund')
/// <summary>
/// 订单状态
/// </summary>
public OrderStatus Status { get; set; } // enum('Pending','Completed','Cancelled','Failed')
/// <summary>
/// 创建时间,订单创建时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp
/// <summary>
/// 更新时间,订单状态更新时间
/// </summary>
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; // timestamp
/// <summary>
/// 订单描述,可选的详细信息
/// </summary>
public string? Description { get; set; } // varchar(255)
//导航属性
public User? User { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace Apimanager_backend.Models
{
public enum OrderStatus
{
Pending,
Completed,
Cancelled,
Failed
}
}

View File

@ -0,0 +1,9 @@
namespace Apimanager_backend.Models
{
public enum OrderType
{
Recharge,
Purchase,
Refund
}
}

View File

@ -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() { }
}
}

View File

@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
namespace Apimanager_backend.Models
{
public class User
{
/// <summary>
/// 用户ID主键自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// 用户名,唯一
/// </summary>
[Required]
public string Username { get; set; } // varchar(20)
/// <summary>
/// 邮箱,唯一
/// </summary>
public string Email { get; set; } // varchar(20)
/// <summary>
/// 密码哈希
/// </summary>
[Required]
[MaxLength(255)]
public string PassHash { get; set; } // varchar(255)
/// <summary>
/// 用户角色
/// </summary>
//public UserRole Role { get; set; } // Enum('Admin','User')
/// <summary>
/// 是否禁用
/// </summary>
public bool IsBan { get; set; } = false; // boolean
/// <summary>
/// 是否删除
/// </summary>
public bool IsDelete { get; set; } = false; // boolean
/// <summary>
/// 余额
/// </summary>
public decimal Balance { get; set; } = 0; // Decimal(10)
/// <summary>
/// api调用凭证
/// </summary>
public string? ApiKey { get; set; } = null;
/// <summary>
/// 创建时间,默认当前时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // Timestamp
//导航属性
public ICollection<UserPackage> Packages { get; set; }
public ICollection<OperationLog> operationLogs { get; set; }
public ICollection<ApiCallLog> CallLogs { get; set; }
public ICollection<Order> Orders { get; set; }
public ICollection<UserRole> Roles { get; set; } = new List<UserRole>();
public User(int id,string username,string email,string passHash)
{
this.Id = id;
this.Username = username;
this.PassHash = passHash;
this.Email = email;
}
public User() { }
}
}

View File

@ -0,0 +1,36 @@
namespace Apimanager_backend.Models
{
public class UserPackage
{/// <summary>
/// 主键,自增
/// </summary>
public int Id { get; set; }
/// <summary>
/// 外键用户ID
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 外键套餐ID
/// </summary>
public int PackageId { get; set; }
/// <summary>
/// 剩余调用次数
/// </summary>
public int RemainingCalls { get; set; }
/// <summary>
/// 套餐过期时间,可用于控制套餐是否过期
/// </summary>
public DateTime ExpiryDate { get; set; } // timestamp
/// <summary>
/// 购买时间
/// </summary>
public DateTime PurchasedAt { get; set; } = DateTime.UtcNow; // timestamp
//导航属性
public User? User { get; set; }
public Apipackage? Package { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -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<ApiContext>(option =>
option.UseMySql(constr, MySqlServerVersion.AutoDetect(constr))
);
builder.Services.AddAllService(configuration);
builder.Services.AddControllers(options =>
{
//模型验证
options.Filters.Add<ModelValidationFilter>();
//Exception过滤器
options.Filters.Add<generalExceptionFilter>();
}).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();

View File

@ -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"
}
}
}
}

View File

@ -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<IAdminService> logger;
public AdminService(ApiContext context, IMapper mapper, ILogger<IAdminService> 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<UserInfoDto> CreateUserAsync(CreateUserDto dto)
{
//添加用户
var user = mapper.Map<User>(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<UserInfoDto>(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<List<UserInfoDto>> 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<List<UserInfoDto>>(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<UserInfoDto> 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<UserInfoDto>(user);
}
#endregion
#region
public async Task<int> UserCountAsync()
{
return await context.Users.CountAsync();
}
#endregion
}
}

View File

@ -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<ApiService> logger;
private readonly IMapper mapper;
public ApiService(ApiContext context, ILogger<ApiService> logger,IMapper mapper)
{
this.context = context;
this.logger = logger;
this.mapper = mapper;
}
#region
public async Task<ApiInfoDto> AddApiAsync(CreateApiInfo dto)
{
var api = mapper.Map<Api>(dto);
context.Apis.Add(api);
await context.SaveChangesAsync();
return mapper.Map<ApiInfoDto>(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<ApiInfoDto> GetApiInfoAsync(int apiId)
{
var apiInfo = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId);
if (apiInfo == null)
{
throw new BaseException(3002,"API不存在");
}
return mapper.Map<ApiInfoDto>(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<ApiInfoDto> 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<ApiInfoDto>(api);
}
#endregion
#region API列表
public async Task<List<ApiInfoDto>> GetApisAsync(int pageIndex, int pageSize, bool desc)
{
IQueryable<Api> 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<ApiInfoDto>>(list);
}
#endregion
#region Api总数
public async Task<int> ApiCountAsync()
{
return await context.Apis.CountAsync();
}
#endregion
}
}

View File

@ -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<IAuthService> 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<AuthService> logger,IConnectionMultiplexer redis,IEmailService emailService)
{
this.apiContext = apiContext;
this.mapper = automapper;
this.logger = logger;
this.redis = redis;
this.emailService = emailService;
}
#region
public async Task<UserInfoDto> 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<UserInfoDto>(user);
}
#endregion
#region
public async Task<UserInfoDto> 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<UserInfoDto>(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}<br>有效期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
}
}

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,50 @@
using Apimanager_backend.Dtos;
namespace Apimanager_backend.Services
{
public interface IAdminService
{
/// <summary>
/// 禁用用户,使其无法登录。
/// </summary>
/// <param name="userId">要禁用的用户ID</param>
/// <returns>异步操作</returns>
Task BanUserAsync(int userId);
/// <summary>
/// 取消禁用用户,恢复登录权限。
/// </summary>
/// <param name="userId">要取消禁用的用户ID</param>
/// <returns>异步操作</returns>
Task UnbanUserAsync(int userId);
/// <summary>
/// 获取分页的用户列表。
/// </summary>
/// <param name="page">要获取的页码从1开始</param>
/// <param name="pageSize">每页的用户数量</param>
/// <param name="desc">是否按降序排序</param>
/// <returns>包含用户信息的 <see cref="List{UserInfoDto}"/></returns>
Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc);
/// <summary>
/// 创建新用户。
/// </summary>
/// <param name="user">包含新用户信息的 <see cref="CreateUserDto"/></param>
/// <returns>创建成功的用户信息 <see cref="UserInfoDto"/></returns>
Task<UserInfoDto> CreateUserAsync(CreateUserDto user);
/// <summary>
/// 删除指定的用户。
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>异步操作</returns>
Task DeleteUserAsync(int userId);
/// <summary>
/// 修改用户信息
/// </summary>
/// <returns></returns>
Task<UserInfoDto> UpdateUserAsync(int userId, AdminUpdateUserDto dto);
/// <summary>
/// 用户总数
/// </summary>
/// <returns></returns>
Task<int> UserCountAsync();
}
}

View File

@ -0,0 +1,60 @@
using Apimanager_backend.Dtos;
namespace Apimanager_backend.Services
{
public interface IApiService
{
/// <summary>
/// 获取api信息
/// </summary>
/// <param name="apiId"></param>
/// <returns></returns>
public Task<ApiInfoDto> GetApiInfoAsync(int apiId);
/// <summary>
/// 添加api
/// </summary>
/// <param name="apiId"></param>
/// <param name="dto"></param>
/// <returns></returns>
public Task<ApiInfoDto> AddApiAsync(CreateApiInfo dto);
/// <summary>
/// 删除api
/// </summary>
/// <param name="apiId"></param>
/// <returns></returns>
public Task DeleteApiAsync(int apiId);
/// <summary>
/// 更新api信息
/// </summary>
/// <param name="apiId"></param>
/// <param name="dto"></param>
/// <returns></returns>
public Task<ApiInfoDto> UpdateApiAsync(int apiId,UpdateApiDto dto);
/// <summary>
/// 启用
/// </summary>
/// <param name="apiId"></param>
/// <returns></returns>
public Task OnApiAsync(int apiId);
/// <summary>
/// 禁用
/// </summary>
/// <param name="apiId"></param>
/// <returns></returns>
public Task OffApiAsync(int apiId);
/// <summary>
/// 获取APi列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="desc"></param>
/// <returns></returns>
public Task<List<ApiInfoDto>> GetApisAsync(int pageIndex, int pageSize, bool desc);
/// <summary>
/// 获取api数量
/// </summary>
/// <returns></returns>
public Task<int> ApiCountAsync();
}
}

View File

@ -0,0 +1,27 @@
using Apimanager_backend.Dtos;
namespace Apimanager_backend.Services
{
public interface IAuthService
{
/// <summary>
/// 登录用户,根据用户名和密码进行身份验证。
/// </summary>
/// <param name="username">用户名</param>
/// <param name="password">密码</param>
/// <returns>包含用户信息的 <see cref="UserInfoBaseDto"/></returns>
Task<UserInfoDto> LoginAsync(string username, string password);
/// <summary>
/// 用户注册邮箱验证码
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
Task SendRegisterCodeAsync(string email);
/// <summary>
/// 用户注册
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto);
}
}

View File

@ -0,0 +1,14 @@
namespace Apimanager_backend.Services
{
public interface IEmailService
{
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="toEmail">收件人邮箱</param>
/// <param name="subject">主题</param>
/// <param name="body">正文</param>
/// <returns></returns>
public Task SendEmailAsync(string toEmail,string subject,string body);
}
}

View File

@ -0,0 +1,65 @@
using Apimanager_backend.Dtos;
using Apimanager_backend.Models;
namespace Apimanager_backend.Services
{
public interface IPackageService
{
/// <summary>
/// 添加套餐
/// </summary>
/// <param name="addPackageDto"></param>
/// <returns></returns>
public Task<PackageInfoDto> AddPackageAsync(AddPackageDto addPackageDto);
/// <summary>
/// 更新套餐信息
/// </summary>
/// <param name="packageId"></param>
/// <param name="updatePackageDto"></param>
/// <returns></returns>
public Task<PackageInfoDto> UpdatePackageAsync(int packageId,UpdatePackageDto updatePackageDto);
/// <summary>
/// 删除套餐
/// </summary>
/// <param name="packageId"></param>
/// <returns></returns>
public Task DeletePackageAsync(int packageId);
/// <summary>
/// 获取套餐列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="desc"></param>
/// <returns></returns>
public Task<List<PackageInfoDto>> GetAllPackagesAsync(int pageIndex,int pageSize,bool desc);
/// <summary>
/// 获取套餐信息
/// </summary>
/// <param name="packageId"></param>
/// <returns></returns>
public Task<PackageInfoDto> PackageInfoByIdAsync(int packageId);
/// <summary>
/// 获取用户所有订阅套餐
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public Task<List<UserPackage>> GetUserPackagesAsync(int userId);
/// <summary>
/// 增加套餐订阅时长
/// </summary>
/// <param name="packageId">套餐Id注意这里不是用户订阅套餐表的Id</param>
/// <param name="time"></param>
/// <param name="isAdmin">判断是否为管理员操作(如果是则不扣除余额)</param>
/// <returns></returns>
public Task<UserPackage> AddUserPackageTimeAsync(int packageId,int userId,TimeSpan time,bool isAdmin);
/// <summary>
/// 扣除套餐订阅时长
/// </summary>
/// <param name="packageId">套餐Id注意这里不是用户订阅套餐表的Id</param>
/// <param name="time"></param>
/// <returns></returns>
public Task<UserPackage> DecuteUserPackageTimeAsync(int packageId,int userId,TimeSpan time);
}
}

View File

@ -0,0 +1,30 @@
namespace Apimanager_backend.Services
{
public interface IRefreshTokenService
{
/// <summary>
/// 创建刷新令牌
/// </summary>
/// <param name="userId">用户id</param>
/// <returns>刷新令牌</returns>
Task<string> CreateRefereshTokenAsync(string userId);
/// <summary>
/// 验证刷新令牌
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>是否验证通过</returns>
Task<bool> ValidateRefreshTokenAsync(string userId,string refreshToken);
/// <summary>
/// 删除刷新令牌
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>是否删除成功</returns>
Task DeleterRefreshTokenAsync(string userId);
/// <summary>
/// 更新刷新令牌有效期
/// </summary>
/// <param name="userId">用户id</param>
/// <returns>是否成功</returns>
Task UpdateRefreshTokenAsync(string userId);
}
}

View File

@ -0,0 +1,10 @@
using Apimanager_backend.Models;
namespace Apimanager_backend.Services
{
public interface ISystemConfigService
{
public Task<SystemConfig> UpdateSystemConfig(string configName,string configBody);
public Task<SystemConfig> GetSystemConfig(string configName);
}
}

View File

@ -0,0 +1,16 @@
using Apimanager_backend.Models;
namespace Apimanager_backend.Services
{
public interface ITokenService
{
/// <summary>
/// 拥护凭证
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="username">用户名</param>
/// <param name="role">角色</param>
/// <returns>token</returns>
string GenerateAccessToken(string userId, List<UserRole> roles);
}
}

View File

@ -0,0 +1,51 @@
using Apimanager_backend.Dtos;
using Apimanager_backend.Models;
using System.Runtime.CompilerServices;
namespace Apimanager_backend.Services
{
public interface IUserService
{
/// <summary>
/// 发送密码重置邮件到指定邮箱。
/// </summary>
/// <param name="email">用户注册的邮箱地址</param>
/// <returns>异步操作</returns>
Task SendResetPasswordEmailAsync(string email);
/// <summary>
/// 重置用户密码,验证重置令牌的有效性并更新密码。
/// </summary>
/// <param name="email">用户邮箱地址</param>
/// <param name="token">重置密码的令牌</param>
/// <param name="newPassword">新的密码</param>
/// <returns>异步操作</returns>
Task ResetPasswordAsync(string email, string code, string newPassword);
/// <summary>
/// 获取用户信息。
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
Task<UserInfoDto> GetUserAsync(int userId);
/// <summary>
/// 更新用户信息。
/// </summary>
/// <param name="user">包含更新信息的 <see cref="UpdateUserDto"/></param>
/// <returns>更新后的 <see cref="UserInfoDto"/></returns>
Task<UserInfoDto> UpdateUserAsync(int userId,UpdateUserDto user);
/// <summary>
/// 检测用户名是否被使用
/// </summary>
/// <param name="username">用户名</param>
/// <returns></returns>
Task<bool> IsUsernameExist(string username);
/// <summary>
/// 检测邮箱是否被使用
/// </summary>
/// <param name="email">邮箱</param>
/// <returns></returns>
Task<bool> IsEmailExist(string email);
}
}

View File

@ -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<PackageService> logger;
private readonly IMapper mapper;
public PackageService(ApiContext apiContext,ILogger<PackageService> logger,IMapper mapper)
{
this.apiContext = apiContext;
this.logger = logger;
this.mapper = mapper;
}
#region
public async Task<PackageInfoDto> AddPackageAsync(AddPackageDto addPackageDto)
{
var packageInfo = mapper.Map<Apipackage>(addPackageDto);
apiContext.Apipackages.Add(packageInfo);
await apiContext.SaveChangesAsync();
return mapper.Map<PackageInfoDto>(packageInfo);
}
#endregion
#region
public async Task<UserPackage> 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<UserPackage> 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<List<PackageInfoDto>> GetAllPackagesAsync(int pageIndex, int pageSize, bool desc)
{
IQueryable<Apipackage> 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<PackageInfoDto>>(list);
}
#endregion
#region
public async Task<List<UserPackage>> GetUserPackagesAsync(int userId)
{
var userpackageInfo = await apiContext.UserPackages.Where(x => x.UserId == userId).ToListAsync();
return userpackageInfo;
}
#endregion
#region
public async Task<PackageInfoDto> PackageInfoByIdAsync(int packageId)
{
var packageInfo = await apiContext.Apipackages.FirstOrDefaultAsync(x => x.Id == packageId);
if(packageInfo == null)
{
throw new BaseException(4004,"套餐未找到");
}
return mapper.Map<PackageInfoDto>(packageInfo);
}
#endregion
#region
public async Task<PackageInfoDto> 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<PackageInfoDto>(packageInfo);
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
}
#endregion
}
}

View File

@ -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<string> 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<bool> 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
}
}

View File

@ -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<SystemConfigService> logger;
public SystemConfigService(ApiContext apiContext,ILogger<SystemConfigService> logger)
{
this.apiContext = apiContext;
this.logger = logger;
}
public async Task<SystemConfig> 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<SystemConfig> GetSystemConfig(string configName)
{
var config = await apiContext.SystemConfigs.FirstOrDefaultAsync(x => x.ConfigName == configName);
if (config == null)
{
throw new BaseException(5006, "配置不存在");
}
return config;
}
}
}

View File

@ -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<UserRole> roles)
{
var jwtSettings = configuration.GetSection("JwtSettings");
// 创建Claims列表包含用户名和角色信息
var claims = new List<Claim>
{
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);
}
}
}

View File

@ -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<IUserService> logger;
private readonly IConnectionMultiplexer redis;
private readonly IEmailService emailService;
private readonly int DbSet = 2;
public UserService(ApiContext apiContext,IMapper automapper,ILogger<IUserService> logger,IConnectionMultiplexer redis,IEmailService emailService)
{
this.apiContext = apiContext;
this.mapper = automapper;
this.logger = logger;
this.redis = redis;
this.emailService = emailService;
}
public async Task<UserInfoDto> 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<UserInfoDto>(user);
}
public async Task<bool> IsEmailExist(string email)
{
return await apiContext.Users.AnyAsync(x => x.Email == email);
}
public async Task<bool> 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}<br>有效期60分钟";
//发送邮件
await emailService.SendEmailAsync(email,subject,body);
}
#endregion
public async Task<UserInfoDto> 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<UserInfoDto>(user);
}
}
}

View File

@ -0,0 +1,6 @@
namespace Apimanager_backend.Tools
{
public class HashHelper
{
}
}

View File

@ -0,0 +1,15 @@
namespace Apimanager_backend.Tools
{
public static class RandomCodeHelper
{
/// <summary>
/// 生成随机数字符串
/// </summary>
/// <returns></returns>
public static string GetRandomCodeStr()
{
Random random = new Random();
return random.Next(10000, 99999).ToString();
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -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"
}
}

93
DatabaseDefine.md Normal file
View File

@ -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]
>
> 注:删除用户时需要保证数据一致性。

64
ErrorCode.md Normal file
View File

@ -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 |

136
README.md
View File

@ -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 3. ##### 支付模块
- [ ] [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:
``` 4. ##### 订单模块
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
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/) 1. **API 控制器**
- [ ] [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)
## 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 - [x] 项目文档 **README.md**
Choose a self-explaining name for your project. - [x] 数据库文档 **Database.md**
- [x] 错误码文档 **ErrorCode.md**
## Description - [x] API定义文档 **APIDefine.md**
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. - [ ] API文档 **[API文档](https://apifox.com/apidoc/shared-8730c2eb-595b-4cf4-941d-d58914d4cd1e)**
## 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.