第三章:项目架构与核心模块解析
目录
- 整体架构设计
- 后端项目结构详解
- 核心层Admin.NET.Core解析
- 应用层Admin.NET.Application解析
- Web层架构解析
- 前端项目结构详解
- 数据流转机制
- 依赖注入与服务注册
1. 整体架构设计
1.1 架构概览
Admin.NET采用经典的分层架构设计,结合前后端分离的开发模式。整体架构可以分为以下几个层次:
┌─────────────────────────────────────────────────────────┐
│ 前端层 (Vue3 + Element Plus) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 视图层 │ │ 状态管理 │ │ 路由层 │ │ API层 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘│ HTTP/HTTPS▼
┌─────────────────────────────────────────────────────────┐
│ Web入口层 (Entry) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 控制器 │ │ 中间件 │ │ 过滤器 │ │ 配置 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ Web核心层 (Web.Core) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 启动配置 │ │ 认证授权 │ │ Swagger │ │ 跨域配置 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 业务服务 │ │ 事件处理 │ │ DTO │ │ 配置 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 核心层 (Core) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 系统服务 │ │ 实体模型 │ │ 仓储层 │ │ 工具类 │ │
│ │ 缓存服务 │ │ 枚举定义 │ │ 扩展方法 │ │ 常量定义 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ SqlSugar │ │ Redis │ │ ES │ │ OSS │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘
1.2 分层职责
前端层(Web):
- 用户界面展示
- 用户交互处理
- 状态管理
- API调用
Web入口层(Admin.NET.Web.Entry):
- 应用程序入口点
- 配置加载
- 中间件注册
- 控制器定义
Web核心层(Admin.NET.Web.Core):
- Furion框架配置
- 认证授权配置
- Swagger文档配置
- 跨域策略配置
应用层(Admin.NET.Application):
- 业务逻辑实现
- 事件处理
- 数据传输对象
- 应用级配置
核心层(Admin.NET.Core):
- 系统级服务
- 实体定义
- 仓储实现
- 基础工具
1.3 设计原则
Admin.NET遵循以下设计原则:
单一职责原则(SRP):
每个类只负责一个功能领域,服务类只处理业务逻辑,实体类只定义数据结构。
开闭原则(OCP):
通过接口和抽象类实现扩展,新增功能不需要修改现有代码。
依赖倒置原则(DIP):
高层模块不依赖低层模块,两者都依赖抽象。通过依赖注入实现。
接口隔离原则(ISP):
使用小而专一的接口,而不是大而全的接口。
2. 后端项目结构详解
2.1 解决方案结构
Admin.NET.sln
├── Admin.NET.Core # 核心层
├── Admin.NET.Application # 应用层
├── Admin.NET.Web.Core # Web核心层
├── Admin.NET.Web.Entry # Web入口层
├── Admin.NET.Test # 单元测试
└── Plugins/ # 插件项目├── Admin.NET.Plugin.ApprovalFlow├── Admin.NET.Plugin.DingTalk├── Admin.NET.Plugin.GoView├── Admin.NET.Plugin.K3Cloud├── Admin.NET.Plugin.ReZero└── Admin.NET.Plugin.WorkWeixin
2.2 项目依赖关系
Admin.NET.Web.Entry├── Admin.NET.Web.Core├── Admin.NET.Application└── Plugins (可选)Admin.NET.Web.Core└── Admin.NET.CoreAdmin.NET.Application└── Admin.NET.CoreAdmin.NET.Core└── 第三方依赖包
2.3 核心依赖包
查看Admin.NET.Core.csproj中的主要依赖:
<ItemGroup><!-- Furion框架 --><PackageReference Include="Furion" Version="4.x.x" /><!-- SqlSugar ORM --><PackageReference Include="SqlSugarCore" Version="5.x.x" /><!-- Redis缓存 --><PackageReference Include="NewLife.Redis" Version="x.x.x" /><!-- Excel导入导出 --><PackageReference Include="Magicodes.IE.Excel" Version="x.x.x" /><!-- 微信SDK --><PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="x.x.x" /><!-- 验证码 --><PackageReference Include="Lazy.Captcha.Core" Version="x.x.x" /><!-- 限流组件 --><PackageReference Include="AspNetCoreRateLimit" Version="x.x.x" /><!-- 任务调度 --><PackageReference Include="Sundial" Version="x.x.x" />
</ItemGroup>
3. 核心层Admin.NET.Core解析
3.1 目录结构
Admin.NET.Core/
├── Attribute/ # 自定义特性
├── Cache/ # 缓存相关
├── Const/ # 常量定义
├── ElasticSearch/ # ES日志
├── Entity/ # 实体定义
├── Enum/ # 枚举定义
├── EventBus/ # 事件总线
├── Extension/ # 扩展方法
├── Hub/ # SignalR Hub
├── Job/ # 后台任务
├── Logging/ # 日志配置
├── Option/ # 配置选项
├── SeedData/ # 种子数据
├── Service/ # 系统服务
├── SignalR/ # SignalR配置
├── SignatureAuth/ # 签名认证
├── SqlSugar/ # ORM配置
├── Update/ # 更新相关
├── Utils/ # 工具类
└── GlobalUsings.cs # 全局using
3.2 实体定义(Entity)
3.2.1 实体基类
// EntityBase.cs
/// <summary>
/// 实体基类
/// </summary>
public abstract class EntityBase : IEntity
{/// <summary>/// 主键Id/// </summary>[SugarColumn(IsPrimaryKey = true, IsIdentity = false)]public virtual long Id { get; set; }
}/// <summary>
/// 实体操作基类
/// </summary>
public abstract class EntityBaseData : EntityBase
{/// <summary>/// 创建时间/// </summary>[SugarColumn(ColumnDescription = "创建时间", IsOnlyIgnoreUpdate = true)]public virtual DateTime? CreateTime { get; set; }/// <summary>/// 更新时间/// </summary>[SugarColumn(ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true)]public virtual DateTime? UpdateTime { get; set; }/// <summary>/// 创建者Id/// </summary>[SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true)]public virtual long? CreateUserId { get; set; }/// <summary>/// 创建者姓名/// </summary>[SugarColumn(ColumnDescription = "创建者姓名", Length = 64, IsOnlyIgnoreUpdate = true)]public virtual string? CreateUserName { get; set; }/// <summary>/// 修改者Id/// </summary>[SugarColumn(ColumnDescription = "修改者Id", IsOnlyIgnoreInsert = true)]public virtual long? UpdateUserId { get; set; }/// <summary>/// 修改者姓名/// </summary>[SugarColumn(ColumnDescription = "修改者姓名", Length = 64, IsOnlyIgnoreInsert = true)]public virtual string? UpdateUserName { get; set; }/// <summary>/// 是否删除/// </summary>[SugarColumn(ColumnDescription = "是否删除")]public virtual bool IsDelete { get; set; } = false;
}/// <summary>
/// 租户实体基类
/// </summary>
public abstract class EntityTenant : EntityBaseData
{/// <summary>/// 租户Id/// </summary>[SugarColumn(ColumnDescription = "租户Id")]public virtual long? TenantId { get; set; }
}
3.2.2 系统核心实体
用户实体(SysUser):
/// <summary>
/// 系统用户表
/// </summary>
[SugarTable(null, "系统用户表")]
[SystemTable]
public class SysUser : EntityTenant
{/// <summary>/// 账号/// </summary>[SugarColumn(ColumnDescription = "账号", Length = 64)][Required, MaxLength(64)]public virtual string Account { get; set; }/// <summary>/// 密码/// </summary>[SugarColumn(ColumnDescription = "密码", Length = 128)][MaxLength(128)][JsonIgnore]public virtual string Password { get; set; }/// <summary>/// 真实姓名/// </summary>[SugarColumn(ColumnDescription = "真实姓名", Length = 64)][MaxLength(64)]public virtual string? RealName { get; set; }/// <summary>/// 昵称/// </summary>[SugarColumn(ColumnDescription = "昵称", Length = 64)][MaxLength(64)]public virtual string? NickName { get; set; }/// <summary>/// 头像/// </summary>[SugarColumn(ColumnDescription = "头像", Length = 512)]public virtual string? Avatar { get; set; }/// <summary>/// 性别-男_1、女_2/// </summary>[SugarColumn(ColumnDescription = "性别")]public virtual GenderEnum Sex { get; set; } = GenderEnum.Male;/// <summary>/// 年龄/// </summary>[SugarColumn(ColumnDescription = "年龄")]public virtual int Age { get; set; }/// <summary>/// 出生日期/// </summary>[SugarColumn(ColumnDescription = "出生日期")]public virtual DateTime? Birthday { get; set; }/// <summary>/// 手机号码/// </summary>[SugarColumn(ColumnDescription = "手机号码", Length = 16)]public virtual string? Phone { get; set; }/// <summary>/// 邮箱/// </summary>[SugarColumn(ColumnDescription = "邮箱", Length = 64)]public virtual string? Email { get; set; }/// <summary>/// 机构Id/// </summary>[SugarColumn(ColumnDescription = "机构Id")]public virtual long OrgId { get; set; }/// <summary>/// 职位Id/// </summary>[SugarColumn(ColumnDescription = "职位Id")]public virtual long PosId { get; set; }/// <summary>/// 账号类型/// </summary>[SugarColumn(ColumnDescription = "账号类型")]public virtual AccountTypeEnum AccountType { get; set; } = AccountTypeEnum.NormalUser;/// <summary>/// 状态/// </summary>[SugarColumn(ColumnDescription = "状态")]public virtual StatusEnum Status { get; set; } = StatusEnum.Enable;/// <summary>/// 排序/// </summary>[SugarColumn(ColumnDescription = "排序")]public virtual int OrderNo { get; set; } = 100;/// <summary>/// 备注/// </summary>[SugarColumn(ColumnDescription = "备注", Length = 256)]public virtual string? Remark { get; set; }
}
菜单实体(SysMenu):
/// <summary>
/// 系统菜单表
/// </summary>
[SugarTable(null, "系统菜单表")]
[SystemTable]
public class SysMenu : EntityBase
{/// <summary>/// 父Id/// </summary>[SugarColumn(ColumnDescription = "父Id")]public long Pid { get; set; }/// <summary>/// 菜单类型(1目录 2菜单 3按钮)/// </summary>[SugarColumn(ColumnDescription = "菜单类型")]public MenuTypeEnum Type { get; set; }/// <summary>/// 菜单名称/// </summary>[SugarColumn(ColumnDescription = "菜单名称", Length = 64)][Required, MaxLength(64)]public string Title { get; set; }/// <summary>/// 路由名称/// </summary>[SugarColumn(ColumnDescription = "路由名称", Length = 64)]public string? Name { get; set; }/// <summary>/// 路由地址/// </summary>[SugarColumn(ColumnDescription = "路由地址", Length = 128)]public string? Path { get; set; }/// <summary>/// 组件路径/// </summary>[SugarColumn(ColumnDescription = "组件路径", Length = 128)]public string? Component { get; set; }/// <summary>/// 重定向/// </summary>[SugarColumn(ColumnDescription = "重定向", Length = 128)]public string? Redirect { get; set; }/// <summary>/// 权限标识/// </summary>[SugarColumn(ColumnDescription = "权限标识", Length = 128)]public string? Permission { get; set; }/// <summary>/// 菜单图标/// </summary>[SugarColumn(ColumnDescription = "菜单图标", Length = 64)]public string? Icon { get; set; }/// <summary>/// 是否内嵌/// </summary>[SugarColumn(ColumnDescription = "是否内嵌")]public bool IsIframe { get; set; }/// <summary>/// 外链链接/// </summary>[SugarColumn(ColumnDescription = "外链链接", Length = 256)]public string? OutLink { get; set; }/// <summary>/// 是否隐藏/// </summary>[SugarColumn(ColumnDescription = "是否隐藏")]public bool IsHide { get; set; }/// <summary>/// 是否缓存/// </summary>[SugarColumn(ColumnDescription = "是否缓存")]public bool IsKeepAlive { get; set; } = true;/// <summary>/// 排序/// </summary>[SugarColumn(ColumnDescription = "排序")]public int OrderNo { get; set; } = 100;/// <summary>/// 状态/// </summary>[SugarColumn(ColumnDescription = "状态")]public StatusEnum Status { get; set; } = StatusEnum.Enable;
}
3.3 服务层(Service)
3.3.1 服务基类
// BaseService.cs
/// <summary>
/// 服务基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
public class BaseService<TEntity> where TEntity : class, new()
{protected readonly SqlSugarRepository<TEntity> _rep;protected readonly IUserManager _userManager;public BaseService(SqlSugarRepository<TEntity> rep, IUserManager userManager){_rep = rep;_userManager = userManager;}/// <summary>/// 获取当前用户Id/// </summary>protected long UserId => _userManager.UserId;/// <summary>/// 获取当前租户Id/// </summary>protected long? TenantId => _userManager.TenantId;/// <summary>/// 是否为超级管理员/// </summary>protected bool IsSuperAdmin => _userManager.SuperAdmin;
}
3.3.2 用户服务示例
/// <summary>
/// 系统用户服务
/// </summary>
[ApiDescriptionSettings(Order = 490)]
public class SysUserService : IDynamicApiController, ITransient
{private readonly SqlSugarRepository<SysUser> _sysUserRep;private readonly SysCacheService _sysCacheService;private readonly SysOrgService _sysOrgService;private readonly SysRoleService _sysRoleService;public SysUserService(SqlSugarRepository<SysUser> sysUserRep,SysCacheService sysCacheService,SysOrgService sysOrgService,SysRoleService sysRoleService){_sysUserRep = sysUserRep;_sysCacheService = sysCacheService;_sysOrgService = sysOrgService;_sysRoleService = sysRoleService;}/// <summary>/// 获取用户分页列表/// </summary>[DisplayName("获取用户分页列表")]public async Task<SqlSugarPagedList<SysUser>> Page(PageUserInput input){var orgIdList = await _sysOrgService.GetChildIdListWithSelfById(input.OrgId);return await _sysUserRep.AsQueryable().WhereIF(!string.IsNullOrWhiteSpace(input.Account), u => u.Account.Contains(input.Account)).WhereIF(!string.IsNullOrWhiteSpace(input.RealName), u => u.RealName.Contains(input.RealName)).WhereIF(!string.IsNullOrWhiteSpace(input.Phone), u => u.Phone.Contains(input.Phone)).WhereIF(input.OrgId > 0, u => orgIdList.Contains(u.OrgId)).WhereIF(input.AccountType.HasValue, u => u.AccountType == input.AccountType).OrderBy(u => u.OrderNo).ToPagedListAsync(input.Page, input.PageSize);}/// <summary>/// 增加用户/// </summary>[ApiDescriptionSettings(Name = "Add"), HttpPost][DisplayName("增加用户")]public async Task<long> Add(AddUserInput input){// 验证账号是否存在var isExist = await _sysUserRep.IsAnyAsync(u => u.Account == input.Account);if (isExist)throw Oops.Oh(ErrorCodeEnum.D1003);var user = input.Adapt<SysUser>();user.Password = CryptogramUtil.Encrypt(input.Password);await _sysUserRep.InsertAsync(user);// 保存用户角色关系await _sysRoleService.GrantUserRole(new GrantUserRoleInput{UserId = user.Id,RoleIdList = input.RoleIdList});return user.Id;}/// <summary>/// 更新用户/// </summary>[ApiDescriptionSettings(Name = "Update"), HttpPost][DisplayName("更新用户")]public async Task Update(UpdateUserInput input){// 验证账号是否存在var isExist = await _sysUserRep.IsAnyAsync(u => u.Account == input.Account && u.Id != input.Id);if (isExist)throw Oops.Oh(ErrorCodeEnum.D1003);var user = input.Adapt<SysUser>();await _sysUserRep.AsUpdateable(user).IgnoreColumns(u => new { u.Password, u.AccountType }).ExecuteCommandAsync();// 清除缓存_sysCacheService.Remove(CacheConst.KeyUserInfo + user.Id);}/// <summary>/// 删除用户/// </summary>[ApiDescriptionSettings(Name = "Delete"), HttpPost][DisplayName("删除用户")]public async Task Delete(DeleteUserInput input){var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id);if (user == null)throw Oops.Oh(ErrorCodeEnum.D1002);// 系统管理员不能删除if (user.AccountType == AccountTypeEnum.SuperAdmin)throw Oops.Oh(ErrorCodeEnum.D1014);await _sysUserRep.FakeDeleteAsync(user);// 清除缓存_sysCacheService.Remove(CacheConst.KeyUserInfo + user.Id);}
}
3.4 缓存服务(Cache)
/// <summary>
/// 系统缓存服务
/// </summary>
public class SysCacheService : ITransient
{private readonly ICache _cache;public SysCacheService(ICache cache){_cache = cache;}/// <summary>/// 获取缓存/// </summary>public T Get<T>(string key){return _cache.Get<T>(key);}/// <summary>/// 设置缓存/// </summary>public void Set(string key, object value, TimeSpan? expiry = null){if (expiry.HasValue)_cache.Set(key, value, expiry.Value);else_cache.Set(key, value);}/// <summary>/// 移除缓存/// </summary>public void Remove(string key){_cache.Remove(key);}/// <summary>/// 检查缓存是否存在/// </summary>public bool Exists(string key){return _cache.ContainsKey(key);}/// <summary>/// 按前缀移除缓存/// </summary>public void RemoveByPrefix(string prefix){var keys = _cache.Keys.Where(k => k.StartsWith(prefix));foreach (var key in keys){_cache.Remove(key);}}
}
3.5 工具类(Utils)
/// <summary>
/// 加密工具类
/// </summary>
public static class CryptogramUtil
{/// <summary>/// SM2加密/// </summary>public static string SM2Encrypt(string plainText){// SM2加密实现}/// <summary>/// SM2解密/// </summary>public static string SM2Decrypt(string cipherText){// SM2解密实现}/// <summary>/// SM3摘要/// </summary>public static string SM3Hash(string data){// SM3摘要实现}/// <summary>/// SM4加密/// </summary>public static string SM4Encrypt(string plainText, string key){// SM4加密实现}/// <summary>/// MD5加密/// </summary>public static string Encrypt(string text){return MD5Encryption.Encrypt(text);}
}
4. 应用层Admin.NET.Application解析
4.1 目录结构
Admin.NET.Application/
├── Configuration/ # 应用配置
├── Const/ # 业务常量
├── Entity/ # 业务实体
├── EventBus/ # 事件处理
├── OpenApi/ # 开放接口
├── GlobalUsings.cs # 全局using
└── Startup.cs # 启动配置
4.2 应用配置
// Startup.cs
[AppStartup(100)]
public class Startup : AppStartup
{public void ConfigureServices(IServiceCollection services){// 注册应用层服务}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){// 配置应用中间件}
}
4.3 业务实体示例
// Entity/Business.cs
/// <summary>
/// 业务表示例
/// </summary>
[SugarTable(null, "业务表示例")]
public class Business : EntityTenant
{/// <summary>/// 业务名称/// </summary>[SugarColumn(ColumnDescription = "业务名称", Length = 64)]public string Name { get; set; }/// <summary>/// 业务编码/// </summary>[SugarColumn(ColumnDescription = "业务编码", Length = 64)]public string Code { get; set; }/// <summary>/// 业务状态/// </summary>[SugarColumn(ColumnDescription = "业务状态")]public StatusEnum Status { get; set; }
}
4.4 事件处理
// EventBus/BusinessEventHandler.cs
/// <summary>
/// 业务事件处理
/// </summary>
public class BusinessEventHandler : IEventSubscriber
{private readonly ILogger<BusinessEventHandler> _logger;public BusinessEventHandler(ILogger<BusinessEventHandler> logger){_logger = logger;}/// <summary>/// 处理业务创建事件/// </summary>[EventSubscribe("Business:Created")]public async Task HandleBusinessCreated(EventHandlerExecutingContext context){var business = (Business)context.Source.Payload;_logger.LogInformation($"业务创建事件:{business.Name}");// 处理业务逻辑await Task.CompletedTask;}
}
5. Web层架构解析
5.1 Web.Core配置
// Startup.cs
[AppStartup(100)]
public class Startup : AppStartup
{public void ConfigureServices(IServiceCollection services){// 配置跨域services.AddCorsAccessor();// 配置控制器services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.AddDateTimeTypeConverters();options.JsonSerializerOptions.PropertyNamingPolicy = null;}).AddInjectWithUnifyResult<AdminResultProvider>();// 配置SignalRservices.AddSignalR().AddNewtonsoftJsonProtocol();// 配置Swaggerservices.AddSwaggerGen(options =>{// Swagger配置});// 配置认证授权services.AddJwt<JwtHandler>(enableGlobalAuthorize: true);// 配置限流services.AddIpRateLimiting();// 配置缓存services.AddCache();// 配置SqlSugarservices.AddSqlSugar();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){// 启用跨域app.UseCorsAccessor();// 启用Swaggerapp.UseSwagger();app.UseSwaggerUI();// 启用静态文件app.UseStaticFiles();// 启用路由app.UseRouting();// 启用认证授权app.UseAuthentication();app.UseAuthorization();// 启用端点app.UseEndpoints(endpoints =>{endpoints.MapControllers();endpoints.MapHub<OnlineUserHub>("/hubs/onlineUser");});}
}
5.2 JWT认证处理
/// <summary>
/// JWT认证处理器
/// </summary>
public class JwtHandler : AppAuthorizeHandler
{public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext){// 验证Token有效性var userId = context.User.FindFirstValue(ClaimConst.UserId);if (string.IsNullOrEmpty(userId))return false;// 验证用户状态var user = await GetUserById(long.Parse(userId));if (user == null || user.Status != StatusEnum.Enable)return false;// 验证权限return await CheckPermission(context, httpContext);}private async Task<bool> CheckPermission(AuthorizationHandlerContext context, DefaultHttpContext httpContext){// 获取当前请求的权限标识var permission = httpContext.GetEndpoint()?.Metadata.GetMetadata<PermissionAttribute>()?.Permission;if (string.IsNullOrEmpty(permission))return true;// 验证用户是否有该权限var userPermissions = await GetUserPermissions(context.User);return userPermissions.Contains(permission);}
}
5.3 统一结果返回
/// <summary>
/// Admin结果提供器
/// </summary>
public class AdminResultProvider : IUnifyResultProvider
{public IActionResult OnSucceeded(ActionExecutedContext context, object data){return new JsonResult(new{code = 200,success = true,data = data,message = "操作成功"});}public IActionResult OnException(ExceptionContext context, Exception exception){return new JsonResult(new{code = GetErrorCode(exception),success = false,data = (object)null,message = exception.Message});}private int GetErrorCode(Exception exception){if (exception is AppFriendlyException appException)return appException.ErrorCode;return 500;}
}
6. 前端项目结构详解
6.1 目录结构
Web/src/
├── api/ # API接口定义
│ ├── system/ # 系统管理接口
│ │ ├── user.ts # 用户接口
│ │ ├── role.ts # 角色接口
│ │ ├── menu.ts # 菜单接口
│ │ └── org.ts # 机构接口
│ ├── platform/ # 平台管理接口
│ └── model/ # 接口类型定义
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── form/ # 表单组件
│ ├── table/ # 表格组件
│ └── dialog/ # 对话框组件
├── directives/ # 自定义指令
├── hooks/ # 组合式函数
│ ├── useTable.ts # 表格Hook
│ ├── useForm.ts # 表单Hook
│ └── useAuth.ts # 权限Hook
├── layout/ # 布局组件
│ ├── navBars/ # 导航栏
│ ├── navMenu/ # 侧边菜单
│ └── main/ # 主内容区
├── router/ # 路由配置
│ ├── index.ts # 路由入口
│ ├── route.ts # 静态路由
│ └── backEnd.ts # 动态路由
├── stores/ # Pinia状态管理
│ ├── modules/ # 状态模块
│ │ ├── user.ts # 用户状态
│ │ ├── menu.ts # 菜单状态
│ │ └── app.ts # 应用状态
│ └── index.ts # 状态入口
├── utils/ # 工具函数
│ ├── request.ts # axios封装
│ ├── storage.ts # 存储工具
│ └── validate.ts # 验证工具
└── views/ # 页面视图├── system/ # 系统管理页面│ ├── user/ # 用户管理│ ├── role/ # 角色管理│ ├── menu/ # 菜单管理│ └── org/ # 机构管理├── platform/ # 平台管理页面└── home/ # 首页
6.2 API接口封装
// api/system/user.ts
import request from '/@/utils/request';/*** 用户管理接口*/
export function userApi() {return {/*** 获取用户分页列表*/getPage(params: PageUserInput) {return request({url: '/api/sysUser/page',method: 'get',params});},/*** 获取用户详情*/getDetail(id: number) {return request({url: '/api/sysUser/detail',method: 'get',params: { id }});},/*** 新增用户*/add(data: AddUserInput) {return request({url: '/api/sysUser/add',method: 'post',data});},/*** 更新用户*/update(data: UpdateUserInput) {return request({url: '/api/sysUser/update',method: 'post',data});},/*** 删除用户*/delete(data: DeleteUserInput) {return request({url: '/api/sysUser/delete',method: 'post',data});}};
}
6.3 Pinia状态管理
// stores/modules/user.ts
import { defineStore } from 'pinia';
import { Session, Local } from '/@/utils/storage';export const useUserStore = defineStore('user', {state: () => ({userInfo: {} as UserInfo,token: '',roles: [] as string[],permissions: [] as string[]}),getters: {// 是否已登录isLogin: (state) => !!state.token,// 获取用户名userName: (state) => state.userInfo.realName || state.userInfo.account},actions: {// 设置TokensetToken(token: string) {this.token = token;Session.set('token', token);},// 设置用户信息setUserInfo(userInfo: UserInfo) {this.userInfo = userInfo;},// 登录async login(loginForm: LoginInput) {const res = await loginApi().login(loginForm);this.setToken(res.data.accessToken);return res;},// 获取用户信息async getUserInfo() {const res = await loginApi().getUserInfo();this.setUserInfo(res.data);this.roles = res.data.roles;this.permissions = res.data.permissions;return res;},// 退出登录async logout() {await loginApi().logout();this.token = '';this.userInfo = {};Session.clear();Local.clear();}}
});
6.4 路由配置
// router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router';
import { staticRoutes, errorRoutes } from './route';
import { useUserStore } from '/@/stores/modules/user';const router = createRouter({history: createWebHashHistory(),routes: [...staticRoutes, ...errorRoutes]
});// 路由前置守卫
router.beforeEach(async (to, from, next) => {const userStore = useUserStore();// 白名单放行if (to.meta.isWhite) {next();return;}// 未登录跳转登录页if (!userStore.isLogin) {next({ path: '/login', query: { redirect: to.fullPath } });return;}// 已登录获取用户信息if (!userStore.userInfo.id) {await userStore.getUserInfo();// 动态添加路由await addDynamicRoutes();next({ ...to, replace: true });return;}next();
});export default router;
7. 数据流转机制
7.1 请求流程
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Vue组件 │ -> │ API接口 │ -> │ Axios │ -> │ 后端API │
└─────────┘ └─────────┘ └─────────┘ └─────────┘│▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Vue组件 │ <- │ 数据处理 │ <- │ Axios │ <- │ 响应结果 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
7.2 后端处理流程
┌─────────────────────────────────────────────────────────┐
│ 请求进入 │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 中间件处理 │
│ 异常处理 -> 日志记录 -> 限流检查 -> 跨域处理 │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 认证授权 │
│ Token验证 -> 用户解析 -> 权限检查 │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 路由匹配 │
│ 找到对应的Controller/Action或动态API │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 参数绑定 │
│ 模型绑定 -> 数据验证 -> 参数转换 │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 业务处理 │
│ Service调用 -> 数据访问 -> 业务逻辑 │
└─────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────┐
│ 结果返回 │
│ 统一包装 -> JSON序列化 -> 响应输出 │
└─────────────────────────────────────────────────────────┘
7.3 数据库操作流程
// 典型的CRUD操作流程
public async Task<SysUser> GetUserById(long id)
{// 1. 先查缓存var cacheKey = CacheConst.KeyUserInfo + id;var user = _cache.Get<SysUser>(cacheKey);if (user != null)return user;// 2. 缓存不存在,查数据库user = await _sysUserRep.AsQueryable().Includes(u => u.Org) // 包含机构信息.Includes(u => u.Pos) // 包含职位信息.FirstAsync(u => u.Id == id);// 3. 写入缓存if (user != null)_cache.Set(cacheKey, user, TimeSpan.FromMinutes(30));return user;
}
8. 依赖注入与服务注册
8.1 自动注入机制
Furion框架提供了自动依赖注入机制,通过实现特定接口自动注册:
// 瞬时注入(每次请求创建新实例)
public class SysUserService : IDynamicApiController, ITransient
{// ...
}// 作用域注入(每个请求范围内共享实例)
public class SomeService : IScoped
{// ...
}// 单例注入(应用程序生命周期内共享实例)
public class CacheService : ISingleton
{// ...
}
8.2 手动注册
// 在Startup.cs中手动注册
public void ConfigureServices(IServiceCollection services)
{// 注册单例services.AddSingleton<ICacheService, CacheService>();// 注册作用域services.AddScoped<IUserContext, UserContext>();// 注册瞬时services.AddTransient<IEmailService, EmailService>();// 注册工厂services.AddTransient<IDbContext>(sp => {var config = sp.GetRequiredService<IConfiguration>();return new DbContext(config.GetConnectionString("Default"));});
}
8.3 服务定位器
// 使用App.GetService获取服务实例
var userService = App.GetService<SysUserService>();
var cache = App.GetService<ICache>();// 获取必需服务(不存在会抛异常)
var requiredService = App.GetRequiredService<SysUserService>();// 在非注入环境使用
public static class ServiceHelper
{public static T GetService<T>() where T : class{return App.GetService<T>();}
}
总结
本章详细介绍了Admin.NET的项目架构和核心模块:
- 整体架构:采用分层架构,包括前端层、Web入口层、Web核心层、应用层和核心层
- 后端项目结构:解决方案包含Core、Application、Web.Core、Web.Entry等项目
- 核心层:包含实体定义、服务实现、缓存、工具类等核心功能
- 应用层:业务实体、业务服务、事件处理等应用级功能
- Web层:认证授权、Swagger、中间件配置等Web相关功能
- 前端项目:API接口、状态管理、路由配置、页面视图等
- 数据流转:从请求到响应的完整处理流程
- 依赖注入:Furion自动注入和手动注册机制
理解项目架构是进行二次开发的基础。在下一章中,我们将深入学习Admin.NET的权限系统和多租户实现。
← 上一章目录下一章 →