惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tenable Blog
WordPress大学
WordPress大学
小众软件
小众软件
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
大猫的无限游戏
大猫的无限游戏
T
The Exploit Database - CXSecurity.com
Attack and Defense Labs
Attack and Defense Labs
Simon Willison's Weblog
Simon Willison's Weblog
C
CXSECURITY Database RSS Feed - CXSecurity.com
量子位
有赞技术团队
有赞技术团队
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
S
Schneier on Security
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
Martin Fowler
Martin Fowler
Recent Announcements
Recent Announcements
Stack Overflow Blog
Stack Overflow Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
阮一峰的网络日志
阮一峰的网络日志
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
F
Full Disclosure
Scott Helme
Scott Helme
GbyAI
GbyAI
N
Netflix TechBlog - Medium
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
Hacker News - Newest:
Hacker News - Newest: "LLM"
Security Latest
Security Latest
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
L
Lohrmann on Cybersecurity
PCI Perspectives
PCI Perspectives
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
D
Docker
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
C
Check Point Blog

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第三章:项目架构与核心模块解析
我才是银古 · 2026-06-21 · via 博客园 - 我才是银古

第三章:项目架构与核心模块解析

目录

  1. 整体架构设计
  2. 后端项目结构详解
  3. 核心层Admin.NET.Core解析
  4. 应用层Admin.NET.Application解析
  5. Web层架构解析
  6. 前端项目结构详解
  7. 数据流转机制
  8. 依赖注入与服务注册

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.Core

Admin.NET.Application
    └── Admin.NET.Core

Admin.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>();

        // 配置SignalR
        services.AddSignalR()
            .AddNewtonsoftJsonProtocol();

        // 配置Swagger
        services.AddSwaggerGen(options =>
        {
            // Swagger配置
        });

        // 配置认证授权
        services.AddJwt<JwtHandler>(enableGlobalAuthorize: true);

        // 配置限流
        services.AddIpRateLimiting();

        // 配置缓存
        services.AddCache();

        // 配置SqlSugar
        services.AddSqlSugar();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 启用跨域
        app.UseCorsAccessor();

        // 启用Swagger
        app.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: {
        // 设置Token
        setToken(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的项目架构和核心模块:

  1. 整体架构:采用分层架构,包括前端层、Web入口层、Web核心层、应用层和核心层
  2. 后端项目结构:解决方案包含Core、Application、Web.Core、Web.Entry等项目
  3. 核心层:包含实体定义、服务实现、缓存、工具类等核心功能
  4. 应用层:业务实体、业务服务、事件处理等应用级功能
  5. Web层:认证授权、Swagger、中间件配置等Web相关功能
  6. 前端项目:API接口、状态管理、路由配置、页面视图等
  7. 数据流转:从请求到响应的完整处理流程
  8. 依赖注入:Furion自动注入和手动注册机制

理解项目架构是进行二次开发的基础。在下一章中,我们将深入学习Admin.NET的权限系统和多租户实现。