commit 619518e7bfdfeea86f39ba5a63f233a7800e965f Author: xiaolipro <2357729423@qq.com> Date: Fri Mar 20 15:15:43 2026 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..783d1c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ + +.idea/ +/.vs +/Fake.sln.DotSettings.user + +Logs/ + +node_modules/ \ No newline at end of file diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..69486f2 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8af4aa8 --- /dev/null +++ b/README.md @@ -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 diff --git a/SimpleAdmin.sln b/SimpleAdmin.sln new file mode 100644 index 0000000..97bdf8b --- /dev/null +++ b/SimpleAdmin.sln @@ -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 diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..386aaa7 --- /dev/null +++ b/src/Program.cs @@ -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() ?? []); + builder.Host.UseAutofac().UseSerilog(); + builder.Services.AddApplication(); + var app = builder.Build(); + app.InitializeApplication(); + await app.RunAsync(); +} +catch (Exception ex) +{ + Log.Fatal(ex, "G"); +} +finally +{ + Log.CloseAndFlush(); +} \ No newline at end of file diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json new file mode 100644 index 0000000..786b6f7 --- /dev/null +++ b/src/Properties/launchSettings.json @@ -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" + } + } + } +} diff --git a/src/SimpleAdmin.Api.csproj b/src/SimpleAdmin.Api.csproj new file mode 100644 index 0000000..6169fe9 --- /dev/null +++ b/src/SimpleAdmin.Api.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + e3d506cf-f5ce-4ba5-bc8e-ab2c179f3034 + + + + + + + + + + + \ No newline at end of file diff --git a/src/SimpleAdmin.Api.http b/src/SimpleAdmin.Api.http new file mode 100644 index 0000000..6da01ba --- /dev/null +++ b/src/SimpleAdmin.Api.http @@ -0,0 +1,6 @@ +@SimpleAdmin.Api_HostAddress = http://localhost:5281 + +GET {{SimpleAdmin.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/src/SimpleAdminApiModule.cs b/src/SimpleAdminApiModule.cs new file mode 100644 index 0000000..444199a --- /dev/null +++ b/src/SimpleAdminApiModule.cs @@ -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(options => + { + options.ApplicationServices2Controller(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() ?? []) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); + }); + }); + + services.Configure(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(); + } +} \ No newline at end of file diff --git a/src/SimpleJwtService.cs b/src/SimpleJwtService.cs new file mode 100644 index 0000000..0f2149f --- /dev/null +++ b/src/SimpleJwtService.cs @@ -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; + +/// +/// 自定义simple-admin auth jwt service +/// +[Dependency(Replace = true)] +public class SimpleJwtService(IOptions jwtOptions, IUserService userService, FakeRbacDbContext rbacDbContext) + : JwtService(jwtOptions, userService) +{ + public override async Task> GenerateClaimsByUserIdAsync(Guid userId, + CancellationToken cancellationToken) + { + var claims = await base.GenerateClaimsByUserIdAsync(userId, cancellationToken); + var mapId = await rbacDbContext.Database.SqlQuery($"select userid as value from user where id = {userId}") + .FirstAsync(cancellationToken); + claims.Add(new Claim("mapId", mapId.ToString())); + return claims; + } +} \ No newline at end of file diff --git a/src/appsettings.Development.json b/src/appsettings.Development.json new file mode 100644 index 0000000..62539e8 --- /dev/null +++ b/src/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore": "Information" + } + } +} \ No newline at end of file diff --git a/src/appsettings.json b/src/appsettings.json new file mode 100644 index 0000000..01a9d8c --- /dev/null +++ b/src/appsettings.json @@ -0,0 +1,8 @@ +{ + "UseLocalConfigs": false, + "Consul": { + "Address": "https://consul.nanafa.com", + // 用户机密 + "Token": "" + } +} \ No newline at end of file diff --git a/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251224143133.jpg b/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251224143133.jpg new file mode 100644 index 0000000..7525f91 Binary files /dev/null and b/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251224143133.jpg differ diff --git a/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251225174451.jpg b/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251225174451.jpg new file mode 100644 index 0000000..12e3f5c Binary files /dev/null and b/src/uploads/avatars/08de4226-3fd4-4a5c-83e5-70e164b15f2f_20251225174451.jpg differ