This commit is contained in:
xiaolipro 2026-03-20 15:15:43 +08:00
commit 619518e7bf
14 changed files with 464 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
.idea/
/.vs
/Fake.sln.DotSettings.user
Logs/
node_modules/

7
NuGet.Config Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="xiaolipro.cn" value="https://baget.xiaolipro.cn/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

180
README.md Normal file
View File

@ -0,0 +1,180 @@
# SimpleAdmin API
基于 Fake 框架的简单管理系统后端 API。
## 技术栈
- **.NET 8.0** - 最新的 .NET 平台
- **Fake Framework** - 模块化应用框架
- **Entity Framework Core** - ORM 框架
- **SQL Server** - 数据库
- **JWT** - 身份认证
- **Swagger** - API 文档
## 功能特性
- ✅ 模块化架构
- ✅ 自动 API 生成(ApplicationService 自动转换为 Controller)
- ✅ RBAC 权限管理
- ✅ JWT 认证
- ✅ Swagger 文档
- ✅ 依赖注入
- ✅ 工作单元
- ✅ 审计日志
## 开始使用
### 前置要求
- .NET 8.0 SDK
- SQL Server (LocalDB 或完整版)
### 配置数据库
修改 `appsettings.json` 中的连接字符串:
```json
{
"ConnectionStrings": {
"Default": "Server=(localdb)\\mssqllocaldb;Database=SimpleAdminDb;Trusted_Connection=true;MultipleActiveResultSets=true"
}
}
```
### 数据库迁移
```bash
# 添加迁移
dotnet ef migrations add InitialCreate --project ../modules/rbac/Fake.Rbac.Infrastructure
# 更新数据库
dotnet ef database update --project ../modules/rbac/Fake.Rbac.Infrastructure
```
### 运行项目
```bash
dotnet run
```
访问 http://localhost:5000 查看 Swagger 文档。
## 默认账号
系统会自动创建默认管理员账号:
- 账号:admin
- 密码:123456
- 角色:超级管理员
**⚠️ 重要:请在生产环境中立即修改默认密码!**
## API 端点
### 认证 API
- `POST /api/auth/login` - 用户登录
- `GET /api/auth/current` - 获取当前用户信息
- `PUT /api/auth/password` - 修改密码
### 用户管理 API
- `GET /api/rbac/user/list` - 获取用户列表
- `GET /api/rbac/user/{id}` - 获取用户详情
- `POST /api/rbac/user` - 创建用户
- `PUT /api/rbac/user/{id}` - 更新用户
- `DELETE /api/rbac/user/{id}` - 删除用户
- `PUT /api/rbac/user/{id}/roles` - 分配角色
- `GET /api/rbac/user/{id}/permissions` - 获取用户权限
### 角色管理 API
- `GET /api/rbac/role/list` - 获取角色列表
- `GET /api/rbac/role/all` - 获取所有角色
- `GET /api/rbac/role/{id}` - 获取角色详情
- `POST /api/rbac/role` - 创建角色
- `PUT /api/rbac/role/{id}` - 更新角色
- `DELETE /api/rbac/role/{id}` - 删除角色
- `PUT /api/rbac/role/{id}/permissions` - 分配权限
### 菜单管理 API
- `GET /api/rbac/menu/tree` - 获取菜单树
- `GET /api/rbac/menu/user/{userId}` - 获取用户菜单
- `GET /api/rbac/menu/{id}` - 获取菜单详情
- `POST /api/rbac/menu` - 创建菜单
- `PUT /api/rbac/menu/{id}` - 更新菜单
- `DELETE /api/rbac/menu/{id}` - 删除菜单
### 权限管理 API
- `GET /api/rbac/permission/all` - 获取所有权限
- `GET /api/rbac/permission/tree` - 获取权限树
## 项目结构
```
SimpleAdmin.Api/
├── Controllers/ # 控制器(手动创建的)
├── Program.cs # 程序入口
├── SimpleAdminApiModule.cs # 主模块配置
├── appsettings.json # 配置文件
└── README.md
```
## Fake 框架特性
### 自动 API 生成
所有继承自 `ApplicationService` 的服务会自动生成对应的 API 端点,无需手动创建 Controller。
### 模块化
通过 `[DependsOn]` 特性声明模块依赖:
```csharp
[DependsOn(
typeof(FakeAspNetCoreModule),
typeof(FakeRbacApplicationModule)
)]
public class SimpleAdminApiModule : FakeModule
{
// ...
}
```
### 依赖注入
框架自动注册服务,支持:
- `ITransientDependency` - 瞬时
- `IScopedDependency` - 作用域
- `ISingletonDependency` - 单例
### 工作单元
应用服务方法自动包装在工作单元中,支持事务管理。
## JWT 配置
JWT 配置在 `appsettings.json` 中:
```json
{
"JwtSettings": {
"SecretKey": "your-secret-key-here",
"ExpiryInMinutes": 60
}
}
```
## CORS 配置
默认允许的前端地址:
- http://localhost:3000
- http://localhost:5173
可在 `SimpleAdminApiModule.cs` 中修改。
## 许可证
MIT

44
SimpleAdmin.sln Normal file
View File

@ -0,0 +1,44 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleAdmin.Api", "src\SimpleAdmin.Api.csproj", "{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "items", "items", "{BE5132AD-C95E-4CB7-AFBD-0D4838AFD48C}"
ProjectSection(SolutionItems) = preProject
NuGet.Config = NuGet.Config
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|x64.ActiveCfg = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|x64.Build.0 = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|x86.ActiveCfg = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Debug|x86.Build.0 = Debug|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|Any CPU.Build.0 = Release|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|x64.ActiveCfg = Release|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|x64.Build.0 = Release|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|x86.ActiveCfg = Release|Any CPU
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F6418A25-D1B2-48E1-BDCA-80CC5262BB18} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
EndGlobal

28
src/Program.cs Normal file
View File

@ -0,0 +1,28 @@
using Serilog;
using SimpleAdmin.Api;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Configuration.AddConsul("idwms/common", source => source.ReloadOnChange = true);
builder.Configuration.AddConsul("idwms/admin", source => source.ReloadOnChange = true);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
try
{
builder.WebHost.UseUrls(builder.Configuration.GetSection("App:Urls").Get<string[]>() ?? []);
builder.Host.UseAutofac().UseSerilog();
builder.Services.AddApplication<SimpleAdminApiModule>();
var app = builder.Build();
app.InitializeApplication();
await app.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "G");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,34 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:22279",
"sslPort": 44353
}
},
"profiles": {
"dev": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "",
"applicationUrl": "http://localhost:5281",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"staging": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "",
"applicationUrl": "http://localhost:5281",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Staging",
"ASPNETCORE_PATHBASE": "/admin"
}
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>e3d506cf-f5ce-4ba5-bc8e-ab2c179f3034</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fake.Autofac" Version="8.0.5.8" />
<PackageReference Include="Fake.Consul" Version="8.0.5.8" />
<PackageReference Include="Fake.Rbac.Application" Version="8.0.5.8" />
<PackageReference Include="Fake.Serilog" Version="8.0.5.8" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
</ItemGroup>
</Project>

6
src/SimpleAdmin.Api.http Normal file
View File

@ -0,0 +1,6 @@
@SimpleAdmin.Api_HostAddress = http://localhost:5281
GET {{SimpleAdmin.Api_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,89 @@
using Fake.AspNetCore;
using Fake.Autofac;
using Fake.Modularity;
using Fake.Rbac.Application;
using Fake.Rbac.Infrastructure;
using Fake.AspNetCore.Auditing;
using Fake.AspNetCore.Mvc;
using Fake.Serilog;
using Microsoft.AspNetCore.HttpOverrides;
namespace SimpleAdmin.Api;
[DependsOn(
typeof(FakeAutofacModule),
typeof(FakeRbacApplicationModule),
typeof(FakeRbacInfrastructureModule),
typeof(FakeSerilogModule)
)]
public class SimpleAdminApiModule : FakeModule
{
private const string DefaultCorsPolicyName = "default";
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = context.Services.GetConfiguration();
// 配置动态 API - 扫描 RBAC 应用服务
services.Configure<FakeAspNetCoreMvcOptions>(options =>
{
options.ApplicationServices2Controller<FakeRbacApplicationModule>(settings =>
{
settings.RootPath = "rbac";
});
});
services.AddFakeAspNetCoreAuditing();
// 添加 Fake Swagger 支持
services.AddFakeSwaggerGen(addSecurity: true);
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder.WithOrigins(configuration.GetSection("App:CorsOrigins").Get<string[]>() ?? [])
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
//新增如下两行
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
public override void ConfigureApplication(ApplicationConfigureContext context)
{
var app = context.GetWebApplication();
// 虚拟路由-网关适配
app.UsePathBase(app.Configuration["ASPNETCORE_PATHBASE"]);
// Swagger
app.UseFakeSwagger();
// 异常处理
app.UseFakeExceptionHandling();
// CORS
app.UseCors("Default");
// 静态文件服务(用于头像等上传文件)
app.UseStaticFiles();
// 路由 - 必须在认证和授权之前
app.UseRouting();
// 认证和授权
app.UseAuthentication();
app.UseAuthorization();
// 端点映射
app.MapControllers();
}
}

28
src/SimpleJwtService.cs Normal file
View File

@ -0,0 +1,28 @@
using System.Security.Claims;
using Fake.AspNetCore.Authentication;
using Fake.DependencyInjection;
using Fake.Rbac.Application.Jwt;
using Fake.Rbac.Application.Services;
using Fake.Rbac.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace SimpleAdmin.Api;
/// <summary>
/// 自定义simple-admin auth jwt service
/// </summary>
[Dependency(Replace = true)]
public class SimpleJwtService(IOptions<JwtOptions> jwtOptions, IUserService userService, FakeRbacDbContext rbacDbContext)
: JwtService(jwtOptions, userService)
{
public override async Task<List<Claim>> GenerateClaimsByUserIdAsync(Guid userId,
CancellationToken cancellationToken)
{
var claims = await base.GenerateClaimsByUserIdAsync(userId, cancellationToken);
var mapId = await rbacDbContext.Database.SqlQuery<int>($"select userid as value from user where id = {userId}")
.FirstAsync(cancellationToken);
claims.Add(new Claim("mapId", mapId.ToString()));
return claims;
}
}

View File

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

8
src/appsettings.json Normal file
View File

@ -0,0 +1,8 @@
{
"UseLocalConfigs": false,
"Consul": {
"Address": "https://consul.nanafa.com",
// 用户机密
"Token": ""
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB