Commit 515fceeb92bacf78716f72ce5111d52846b7a1e8

Authored by “wangming”
0 parents

框架初始化

Showing 2204 changed files with 159987 additions and 0 deletions

Too many changes.

To preserve performance only 100 of 2204 files are displayed.

.DS_Store 0 → 100644
No preview for this file type
.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +## Ignore Visual Studio temporary files, build results, and
  2 +## files generated by popular Visual Studio add-ons.
  3 +
  4 +# User-specific files
  5 +*.suo
  6 +*.user
  7 +*.userosscache
  8 +*.sln.docstates
  9 +
  10 +# User-specific files (MonoDevelop/Xamarin Studio)
  11 +*.userprefs
  12 +
  13 +# Build results
  14 +[Dd]ebug/
  15 +[Dd]ebugPublic/
  16 +[Rr]elease/
  17 +[Rr]eleases/
  18 +x64/
  19 +x86/
  20 +bld/
  21 +[Bb]in/
  22 +[Oo]bj/
  23 +[L]og/
  24 +
  25 +# Visual Studio 2015 cache/options directory
  26 +.vs/
  27 +# Uncomment if you have tasks that create the project's static files in wwwroot
  28 +#wwwroot/
  29 +
  30 +# MSTest test Results
  31 +[Tt]est[Rr]esult*/
  32 +[Bb]uild[Ll]og.*
  33 +
  34 +# NUNIT
  35 +*.VisualState.xml
  36 +TestResult.xml
  37 +
  38 +# Build Results of an ATL Project
  39 +[Dd]ebugPS/
  40 +[Rr]eleasePS/
  41 +dlldata.c
  42 +
  43 +# DNX
  44 +project.lock.json
  45 +artifacts/
  46 +
  47 +*_i.c
  48 +*_p.c
  49 +*_i.h
  50 +*.ilk
  51 +*.meta
  52 +*.obj
  53 +*.pch
  54 +*.pdb
  55 +*.pgc
  56 +*.pgd
  57 +*.rsp
  58 +*.sbr
  59 +*.tlb
  60 +*.tli
  61 +*.tlh
  62 +*.tmp
  63 +*.tmp_proj
  64 +*.log
  65 +*.vspscc
  66 +*.vssscc
  67 +.builds
  68 +*.pidb
  69 +*.svclog
  70 +*.scc
  71 +
  72 +# Chutzpah Test files
  73 +_Chutzpah*
  74 +
  75 +# Visual C++ cache files
  76 +ipch/
  77 +*.aps
  78 +*.ncb
  79 +*.opendb
  80 +*.opensdf
  81 +*.sdf
  82 +*.cachefile
  83 +*.VC.db
  84 +*.VC.VC.opendb
  85 +
  86 +# Visual Studio profiler
  87 +*.psess
  88 +*.vsp
  89 +*.vspx
  90 +*.sap
  91 +
  92 +# TFS 2012 Local Workspace
  93 +$tf/
  94 +
  95 +# Guidance Automation Toolkit
  96 +*.gpState
  97 +
  98 +# ReSharper is a .NET coding add-in
  99 +_ReSharper*/
  100 +*.[Rr]e[Ss]harper
  101 +*.DotSettings.user
  102 +
  103 +# JustCode is a .NET coding add-in
  104 +.JustCode
  105 +
  106 +# TeamCity is a build add-in
  107 +_TeamCity*
  108 +
  109 +# DotCover is a Code Coverage Tool
  110 +*.dotCover
  111 +
  112 +# NCrunch
  113 +_NCrunch_*
  114 +.*crunch*.local.xml
  115 +nCrunchTemp_*
  116 +
  117 +# MightyMoose
  118 +*.mm.*
  119 +AutoTest.Net/
  120 +
  121 +# Web workbench (sass)
  122 +.sass-cache/
  123 +
  124 +# Installshield output folder
  125 +[Ee]xpress/
  126 +
  127 +# DocProject is a documentation generator add-in
  128 +DocProject/buildhelp/
  129 +DocProject/Help/*.HxT
  130 +DocProject/Help/*.HxC
  131 +DocProject/Help/*.hhc
  132 +DocProject/Help/*.hhk
  133 +DocProject/Help/*.hhp
  134 +DocProject/Help/Html2
  135 +DocProject/Help/html
  136 +
  137 +# Click-Once directory
  138 +publish/
  139 +
  140 +# Publish Web Output
  141 +*.[Pp]ublish.xml
  142 +*.azurePubxml
  143 +# TODO: Comment the next line if you want to checkin your web deploy settings
  144 +# but database connection strings (with potential passwords) will be unencrypted
  145 +*.pubxml
  146 +*.publishproj
  147 +
  148 +# Microsoft Azure Web App publish settings. Comment the next line if you want to
  149 +# checkin your Azure Web App publish settings, but sensitive information contained
  150 +# in these scripts will be unencrypted
  151 +PublishScripts/
  152 +
  153 +# NuGet Packages
  154 +*.nupkg
  155 +# The packages folder can be ignored because of Package Restore
  156 +**/packages/*
  157 +
  158 +# 把 Yi.Vben5.Vue3 下的 packages 目录重新放出来
  159 +!**/Yi.Vben5.Vue3/packages/
  160 +!**/Yi.Vben5.Vue3/packages/**
  161 +# except build/, which is used as an MSBuild target.
  162 +!**/packages/build/
  163 +# Uncomment if necessary however generally it will be regenerated when needed
  164 +#!**/packages/repositories.config
  165 +# NuGet v3's project.json files produces more ignoreable files
  166 +*.nuget.props
  167 +*.nuget.targets
  168 +
  169 +# Microsoft Azure Build Output
  170 +csx/
  171 +*.build.csdef
  172 +
  173 +# Microsoft Azure Emulator
  174 +ecf/
  175 +rcf/
  176 +
  177 +# Windows Store app package directories and files
  178 +AppPackages/
  179 +BundleArtifacts/
  180 +Package.StoreAssociation.xml
  181 +_pkginfo.txt
  182 +
  183 +# Visual Studio cache files
  184 +# files ending in .cache can be ignored
  185 +*.[Cc]ache
  186 +# but keep track of directories ending in .cache
  187 +!*.[Cc]ache/
  188 +
  189 +# Others
  190 +ClientBin/
  191 +~$*
  192 +*~
  193 +*.dbmdl
  194 +*.dbproj.schemaview
  195 +*.pfx
  196 +*.publishsettings
  197 +node_modules/
  198 +orleans.codegen.cs
  199 +
  200 +# Since there are multiple workflows, uncomment next line to ignore bower_components
  201 +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
  202 +#bower_components/
  203 +
  204 +# RIA/Silverlight projects
  205 +Generated_Code/
  206 +
  207 +# Backup & report files from converting an old project file
  208 +# to a newer Visual Studio version. Backup files are not needed,
  209 +# because we have git ;-)
  210 +_UpgradeReport_Files/
  211 +Backup*/
  212 +UpgradeLog*.XML
  213 +UpgradeLog*.htm
  214 +
  215 +# SQL Server files
  216 +*.mdf
  217 +*.ldf
  218 +
  219 +# Business Intelligence projects
  220 +*.rdl.data
  221 +*.bim.layout
  222 +*.bim_*.settings
  223 +
  224 +# Microsoft Fakes
  225 +FakesAssemblies/
  226 +
  227 +# GhostDoc plugin setting file
  228 +*.GhostDoc.xml
  229 +
  230 +# Node.js Tools for Visual Studio
  231 +.ntvs_analysis.dat
  232 +
  233 +# Visual Studio 6 build log
  234 +*.plg
  235 +
  236 +# Visual Studio 6 workspace options file
  237 +*.opt
  238 +
  239 +# Visual Studio LightSwitch build output
  240 +**/*.HTMLClient/GeneratedArtifacts
  241 +**/*.DesktopClient/GeneratedArtifacts
  242 +**/*.DesktopClient/ModelManifest.xml
  243 +**/*.Server/GeneratedArtifacts
  244 +**/*.Server/ModelManifest.xml
  245 +_Pvt_Extensions
  246 +
  247 +# Paket dependency manager
  248 +.paket/paket.exe
  249 +paket-files/
  250 +
  251 +# FAKE - F# Make
  252 +.fake/
  253 +
  254 +# JetBrains Rider
  255 +.idea/
  256 +*.sln.iml
  257 +
  258 +# BookStore
  259 +src/Acme.BookStore.Web/Logs/*
  260 +src/Acme.BookStore.Web.Host/Logs/*
  261 +src/Acme.BookStore.AuthServer/Logs/*
  262 +src/Acme.BookStore.HttpApi.Host/Logs/*
  263 +src/Acme.BookStore.HttpApi.HostWithIds/Logs/*
  264 +src/Acme.BookStore.DbMigrator/Logs/*
  265 +src/Acme.BookStore.Blazor.Server/Logs/*
  266 +src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
  267 +
  268 +# Use abp install-libs to restore.
  269 +**/wwwroot/libs/*
  270 +public
  271 +dist
  272 +.vscode
  273 +/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
  274 +/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Production.json
  275 +/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Development.json
  276 +/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Production.json
  277 +/Yi.Abp.Net8/tool/Yi.Abp.Tool.Web/appsettings.Development.json
  278 +database_backup
  279 +/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Staging.json
  280 +/Yi.Abp.Net8/src/Yi.Abp.Web/logs/
  281 +/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db
  282 +
  283 +package-lock.json
... ...
LICENSE 0 → 100644
  1 +++ a/LICENSE
  1 +MIT License
  2 +
  3 +Copyright (c) 2023 橙子
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
... ...
README-Docker.md 0 → 100644
  1 +++ a/README-Docker.md
  1 +# 🍉Docker 构建说明
  2 +
  3 +## 🍊后端
  4 +执行目录:Yi\Yi.Abp.Net8
  5 +
  6 +#### 🍊启动
  7 +//不带配置文件
  8 +docker run -d --name yi.admin -p 19001:19001 jiftcc/yi.admin:1.0.0
  9 +
  10 +//带配置文件
  11 +docker run -d --name yi.admin -p 19001:19001 -v D:/code/csharp/source/Yi/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.json:/app/appsettings.json jiftcc/yi.admin:1.0.0
  12 +
  13 +
  14 +#### 🍊完整代码编译
  15 +docker build -t jiftcc/yi.admin:1.0.0 -f Dockerfile .
  16 +
  17 +#### 🍊快速产物编译
  18 +docker build -t jiftcc/yi.admin:1.0.0 -f DockerfileFast .
  19 +
... ...
README.md 0 → 100644
  1 +++ a/README.md
  1 +# Yi 框架
  2 +---
  3 +
  4 +## 📖 简介
  5 +
  6 +**Yi Framework**(意框架)是一个基于 **.NET 8 + ABP.vNext + SqlSugar** 的 DDD 领域驱动设计后端开源框架。
  7 +
  8 +### 核心理念
  9 +
  10 +- **简单易用**:面向用户的快速开发框架,新人友好
  11 +- **源码开放**:框架以源码形式提供,不打包,方便二开和学习
  12 +- **模块化设计**:基于 ABP.vNext 模块化架构,按需引用
  13 +- **开箱即用**:内置权限管理、多租户、CRUD 等常用功能
  14 +
  15 +### 技术栈
  16 +
  17 +**后端**
  18 +- .NET 8.0
  19 +- ABP.vNext(动态 API、模块化、依赖注入)
  20 +- SqlSugar(ORM)
  21 +- JWT 鉴权
  22 +- Serilog 日志
  23 +- Mapster 对象映射
  24 +
  25 +**前端**
  26 +- Vue 3.2
  27 +- Vben Admin(企业级中后台前端解决方案)
  28 +
  29 +---
  30 +
  31 +## 🚀 快速开始
  32 +
  33 +### 环境要求
  34 +
  35 +- **.NET SDK 8.0** 或更高版本
  36 +- **Visual Studio 2022** 或 **VS Code**
  37 +- **Node.js 20.10+**(前端开发需要)
  38 +- **数据库**:SQLite(默认)/ MySQL / SQL Server / PostgreSQL / Oracle
  39 +
  40 +### 第一步:克隆项目
  41 +
  42 +```bash
  43 +git clone https://gitee.com/ccnetcore/Yi.git
  44 +cd Yi
  45 +```
  46 +
  47 +### 第二步:配置数据库
  48 +
  49 +编辑 `Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.json`:
  50 +
  51 +```json
  52 +{
  53 + "DbConnOptions": {
  54 + "Url": "server=localhost;port=3306;database=yi_db;uid=root;pwd=123456;CharSet=utf8mb4;",
  55 + "DbType": "Mysql",
  56 + "EnabledCodeFirst": true, // 是否通过代码优先创建数据库表结构
  57 + "EnabledDbSeed": true, // 是否启用数据库种子数据(初始化数据)
  58 + "EnabledSqlLog": true, // 是否启用SQL日志记录
  59 + "EnabledSaasMultiTenancy": false, // 是否启用多租户
  60 + "EnabledConcurrencyException": false // 是否启用并发异常处理
  61 + }
  62 +}
  63 +```
  64 +
  65 +**支持的数据库类型**:
  66 +- `Sqlite`(默认,无需配置)
  67 +- `Mysql`
  68 +- `Sqlserver`
  69 +- `PostgreSQL`
  70 +- `Oracle`
  71 +
  72 +### 第三步:启动后端
  73 +
  74 +1. 使用 Visual Studio 打开 `Yi.Abp.Net8/Yi.Abp.sln`
  75 +2. 设置启动项目为 `Yi.Abp.Web`
  76 +3. 按 `F5` 启动项目
  77 +
  78 +启动成功后,浏览器会自动打开 Swagger 文档:`http://localhost:19001/swagger`
  79 +
  80 +**默认管理员账号**:`cc` / `123456`
  81 +
  82 +> 💡 **提示**:
  83 +> - 首次启动会自动创建数据库表(CodeFirst)
  84 +> - 会自动初始化种子数据(用户、角色等)
  85 +> - 接口会自动分组,点击右上角的分组名称查看不同模块的接口
  86 +
  87 +### 第四步:启动前端(可选)
  88 +
  89 +```bash
  90 +cd Yi.Vben5.Vue3
  91 +pnpm install
  92 +pnpm dev:antd
  93 +```
  94 +
  95 +前端地址:`http://localhost:18000`
  96 +
  97 +---
  98 +
  99 +## 📁 项目结构
  100 +
  101 +```
  102 +Yi/
  103 +├── Yi.Abp.Net8/ # 后端项目
  104 +│ ├── framework/ # 框架核心代码(基础设施)
  105 +│ │ ├── Yi.Framework.SqlSugarCore/ # SqlSugar ORM 封装
  106 +│ │ ├── Yi.Framework.Ddd.Application/ # DDD 应用层基类
  107 +│ │ └── ...
  108 +│ ├── module/ # 业务模块目录(所有业务代码都在这里)
  109 +│ │ ├── rbac/ # 权限管理模块(内置)
  110 +│ │ ├── tenant-management/# 租户管理模块(内置)
  111 +│ │ ├── antis-erp/ # 示例业务模块
  112 +│ │ └── your-module/ # 你的业务模块(在这里创建)
  113 +│ └── src/ # 框架启动项目
  114 +│ └── Yi.Abp.Web/ # Web 层(启动项目,仅配置)
  115 +├── Yi.Vben5.Vue3/ # 前端项目
  116 +└── Yi.Doc.Md/ # 框架文档
  117 +```
  118 +
  119 +### 模块结构(每个业务模块都包含以下分层)
  120 +
  121 +```
  122 +your-module/
  123 +├── YourModule.Domain.Shared/ # 领域共享层(枚举、常量)
  124 +├── YourModule.Domain/ # 领域层(实体、领域服务)
  125 +├── YourModule.Application.Contracts/ # 应用抽象层(接口、DTO)
  126 +├── YourModule.Application/ # 应用层(服务实现)
  127 +└── YourModule.SqlSugarCore/ # 基础设施层(数据库上下文)
  128 +```
  129 +
  130 +### 分层架构(DDD)
  131 +
  132 +```
  133 +┌─────────────────────────────────┐
  134 +│ Web 层(Yi.Abp.Web) │ ← 启动项目、中间件配置
  135 +├─────────────────────────────────┤
  136 +│ 应用层(Application) │ ← 业务逻辑、服务实现
  137 +├─────────────────────────────────┤
  138 +│ 应用抽象层(Application.Contracts)│ ← 接口定义、DTO
  139 +├─────────────────────────────────┤
  140 +│ 领域层(Domain) │ ← 实体、领域服务
  141 +├─────────────────────────────────┤
  142 +│ 领域共享层(Domain.Shared) │ ← 枚举、常量、共享 DTO
  143 +├─────────────────────────────────┤
  144 +│ 基础设施层(SqlSugarCore) │ ← ORM、仓储实现
  145 +└─────────────────────────────────┘
  146 +```
  147 +
  148 +> 💡 **重要**:
  149 +> - **所有业务代码都应该放在 `module` 目录下创建自己的模块**
  150 +> - `src` 目录只用于框架启动配置,不存放业务代码
  151 +> - 每个模块都是独立的,可以按需引用
  152 +
  153 +---
  154 +
  155 +## 🎯 核心功能
  156 +
  157 +### 1. 动态 API
  158 +
  159 +框架基于 ABP.vNext,自动将应用服务转换为 RESTful API,无需手写 Controller。
  160 +
  161 +**示例**:
  162 +```csharp
  163 +// 在 Application 层创建服务
  164 +public class NewsService : YiCrudAppService<...> { }
  165 +
  166 +// 自动生成 API:
  167 +// GET /api/app/news - 查询列表
  168 +// GET /api/app/news/{id} - 查询详情
  169 +// POST /api/app/news - 创建
  170 +// PUT /api/app/news/{id} - 更新
  171 +// DELETE /api/app/news/{id} - 删除
  172 +```
  173 +
  174 +### 2. CRUD 快速开发
  175 +
  176 +继承 `YiCrudAppService` 即可自动获得完整的增删改查功能。
  177 +
  178 +### 3. 权限管理(RBAC)
  179 +
  180 +内置完整的权限管理系统:
  181 +- 用户管理
  182 +- 角色管理
  183 +- 菜单管理
  184 +- 部门管理
  185 +- 岗位管理
  186 +- 字典管理
  187 +- 操作日志
  188 +- 登录日志
  189 +
  190 +### 4. 多租户支持
  191 +
  192 +支持 SaaS 多租户架构,可配置是否启用。
  193 +
  194 +### 5. 模块化架构
  195 +
  196 +采用 ABP.vNext 模块化设计,功能模块化,按需引用。
  197 +
  198 +---
  199 +
  200 +## 💻 开发指南
  201 +
  202 +### 创建自己的业务模块
  203 +
  204 +**重要**:所有业务代码都应该在 `module` 目录下创建自己的模块,而不是放在 `src` 目录。
  205 +
  206 +#### 步骤一:创建模块目录结构
  207 +
  208 +在 `Yi.Abp.Net8/module/` 目录下创建你的模块文件夹,例如:`your-module/`
  209 +
  210 +模块应包含以下项目(参考 `antis-erp` 模块):
  211 +- `YourModule.Domain.Shared` - 领域共享层
  212 +- `YourModule.Domain` - 领域层
  213 +- `YourModule.Application.Contracts` - 应用抽象层
  214 +- `YourModule.Application` - 应用层
  215 +- `YourModule.SqlSugarCore` - 基础设施层
  216 +
  217 +#### 步骤二:创建模块文件
  218 +
  219 +每个项目都需要一个 `Module` 类,例如:
  220 +- `YourModuleDomainSharedModule.cs`
  221 +- `YourModuleDomainModule.cs`
  222 +- `YourModuleApplicationContractsModule.cs`
  223 +- `YourModuleApplicationModule.cs`
  224 +- `YourModuleSqlSugarCoreModule.cs`
  225 +
  226 +#### 步骤三:注册模块依赖
  227 +
  228 +在 `Yi.Abp.Web/YiAbpWebModule.cs` 中注册你的模块:
  229 +
  230 +```csharp
  231 +[DependsOn(
  232 + typeof(YourModuleApplicationModule), // 添加你的应用模块
  233 + // ... 其他依赖
  234 +)]
  235 +public class YiAbpWebModule : AbpModule
  236 +{
  237 + // 注册动态 API
  238 + PreConfigure<AbpAspNetCoreMvcOptions>(options =>
  239 + {
  240 + options.ConventionalControllers.Create(
  241 + typeof(YourModuleApplicationModule).Assembly,
  242 + options => options.RemoteServiceName = "你的模块名称"
  243 + );
  244 + });
  245 +}
  246 +```
  247 +
  248 +### 创建一个完整的 CRUD 功能
  249 +
  250 +以创建"新闻管理"功能为例(在 `your-module` 模块中):
  251 +
  252 +#### 1. 创建实体(Domain 层)
  253 +
  254 +在 `YourModule.Domain/Entities/` 创建:
  255 +
  256 +```csharp
  257 +using SqlSugar;
  258 +using Volo.Abp.Auditing;
  259 +using Volo.Abp.Domain.Entities;
  260 +using Yi.Framework.Core.Data;
  261 +
  262 +namespace YourModule.Domain.Entities
  263 +{
  264 + [SugarTable("News")]
  265 + public class NewsAggregateRoot : AggregateRoot<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
  266 + {
  267 + [SugarColumn(IsPrimaryKey = true)]
  268 + public override Guid Id { get; protected set; }
  269 +
  270 + public bool IsDeleted { get; set; }
  271 + public DateTime CreationTime { get; set; } = DateTime.Now;
  272 + public Guid? CreatorId { get; set; }
  273 + public Guid? LastModifierId { get; set; }
  274 + public DateTime? LastModificationTime { get; set; }
  275 + public int OrderNum { get; set; } = 0;
  276 + public bool State { get; set; } = true;
  277 +
  278 + [SugarColumn(ColumnName = "Title")]
  279 + public string Title { get; set; } = string.Empty;
  280 +
  281 + [SugarColumn(ColumnName = "Content", ColumnDataType = "text")]
  282 + public string Content { get; set; } = string.Empty;
  283 + }
  284 +}
  285 +```
  286 +
  287 +#### 2. 创建 DTO(Application.Contracts 层)
  288 +
  289 +在 `YourModule.Application.Contracts/Dtos/News/` 创建:
  290 +
  291 +```csharp
  292 +// NewsGetOutputDto.cs - 详情输出 DTO
  293 +using Volo.Abp.Application.Dtos;
  294 +
  295 +namespace YourModule.Application.Contracts.Dtos.News
  296 +{
  297 + public class NewsGetOutputDto : EntityDto<Guid>
  298 + {
  299 + public string Title { get; set; }
  300 + public string Content { get; set; }
  301 + public DateTime CreationTime { get; set; }
  302 + }
  303 +}
  304 +
  305 +// NewsGetListOutputDto.cs - 列表输出 DTO
  306 +public class NewsGetListOutputDto : EntityDto<Guid>
  307 +{
  308 + public string Title { get; set; }
  309 + public DateTime CreationTime { get; set; }
  310 +}
  311 +
  312 +// NewsGetListInputVo.cs - 查询参数
  313 +using Yi.Framework.Ddd.Application.Contracts;
  314 +
  315 +public class NewsGetListInputVo : PagedAllResultRequestDto
  316 +{
  317 + public string? Title { get; set; }
  318 +}
  319 +
  320 +// NewsCreateInputVo.cs - 创建输入
  321 +namespace YourModule.Application.Contracts.Dtos.News
  322 +{
  323 + public class NewsCreateInputVo
  324 + {
  325 + public string Title { get; set; }
  326 + public string Content { get; set; }
  327 + }
  328 +}
  329 +
  330 +// NewsUpdateInputVo.cs - 更新输入
  331 +public class NewsUpdateInputVo
  332 +{
  333 + public string Title { get; set; }
  334 + public string Content { get; set; }
  335 +}
  336 +```
  337 +
  338 +#### 3. 创建服务接口(Application.Contracts 层)
  339 +
  340 +在 `YourModule.Application.Contracts/IServices/` 创建:
  341 +
  342 +```csharp
  343 +using Yi.Framework.Ddd.Application.Contracts;
  344 +using YourModule.Application.Contracts.Dtos.News;
  345 +
  346 +namespace YourModule.Application.Contracts.IServices
  347 +{
  348 + public interface INewsService : IYiCrudAppService<
  349 + NewsGetOutputDto,
  350 + NewsGetListOutputDto,
  351 + Guid,
  352 + NewsGetListInputVo,
  353 + NewsCreateInputVo,
  354 + NewsUpdateInputVo>
  355 + {
  356 + }
  357 +}
  358 +```
  359 +
  360 +#### 4. 实现服务(Application 层)
  361 +
  362 +在 `YourModule.Application/Services/` 创建:
  363 +
  364 +```csharp
  365 +using SqlSugar;
  366 +using Volo.Abp.Application.Dtos;
  367 +using Yi.Framework.Ddd.Application;
  368 +using Yi.Framework.SqlSugarCore.Abstractions;
  369 +using YourModule.Application.Contracts.Dtos.News;
  370 +using YourModule.Application.Contracts.IServices;
  371 +using YourModule.Domain.Entities;
  372 +
  373 +namespace YourModule.Application.Services
  374 +{
  375 + public class NewsService : YiCrudAppService<
  376 + NewsAggregateRoot,
  377 + NewsGetOutputDto,
  378 + NewsGetListOutputDto,
  379 + Guid,
  380 + NewsGetListInputVo,
  381 + NewsCreateInputVo,
  382 + NewsUpdateInputVo>,
  383 + INewsService
  384 + {
  385 + private ISqlSugarRepository<NewsAggregateRoot, Guid> _repository;
  386 +
  387 + public NewsService(ISqlSugarRepository<NewsAggregateRoot, Guid> repository)
  388 + : base(repository)
  389 + {
  390 + _repository = repository;
  391 + }
  392 +
  393 + // 自定义查询逻辑(可选)
  394 + public override async Task<PagedResultDto<NewsGetListOutputDto>> GetListAsync(NewsGetListInputVo input)
  395 + {
  396 + RefAsync<int> total = 0;
  397 + var entities = await _repository._DbQueryable
  398 + .WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title!))
  399 + .OrderByDescending(x => x.CreationTime)
  400 + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  401 +
  402 + return new PagedResultDto<NewsGetListOutputDto>(
  403 + total,
  404 + await MapToGetListOutputDtosAsync(entities)
  405 + );
  406 + }
  407 + }
  408 +}
  409 +```
  410 +
  411 +#### 5. 注册模块和动态 API
  412 +
  413 +在 `Yi.Abp.Web/YiAbpWebModule.cs` 中:
  414 +
  415 +```csharp
  416 +// 1. 添加模块依赖
  417 +[DependsOn(
  418 + typeof(YourModuleApplicationModule), // 添加你的模块
  419 + // ... 其他依赖
  420 +)]
  421 +
  422 +// 2. 注册动态 API
  423 +PreConfigure<AbpAspNetCoreMvcOptions>(options =>
  424 +{
  425 + options.ConventionalControllers.Create(
  426 + typeof(YourModuleApplicationModule).Assembly,
  427 + options => options.RemoteServiceName = "你的模块名称"
  428 + );
  429 +});
  430 +```
  431 +
  432 +#### 6. 完成!
  433 +
  434 +启动项目后,自动生成以下 API:
  435 +- `GET /api/app/news` - 分页查询
  436 +- `GET /api/app/news/{id}` - 查询详情
  437 +- `POST /api/app/news` - 创建
  438 +- `PUT /api/app/news/{id}` - 更新
  439 +- `DELETE /api/app/news/{id}` - 删除
  440 +
  441 +数据库表会在首次启动时自动创建(CodeFirst)。
  442 +
  443 +---
  444 +
  445 +## ⚙️ 配置说明
  446 +
  447 +### 数据库配置
  448 +
  449 +```json
  450 +{
  451 + "DbConnOptions": {
  452 + "Url": "数据库连接字符串",
  453 + "DbType": "Mysql", // 数据库类型
  454 + "EnabledCodeFirst": true, // 是否自动创建表
  455 + "EnabledDbSeed": true, // 是否初始化种子数据
  456 + "EnabledSqlLog": true, // 是否记录SQL日志
  457 + "EnabledSaasMultiTenancy": false, // 是否启用多租户
  458 + "EnabledConcurrencyException": false // 是否启用乐观锁
  459 + }
  460 +}
  461 +```
  462 +
  463 +### JWT 配置
  464 +
  465 +```json
  466 +{
  467 + "JwtOptions": {
  468 + "Issuer": "https://ccnetcore.com",
  469 + "Audience": "https://ccnetcore.com",
  470 + "SecurityKey": "你的密钥",
  471 + "ExpiresMinuteTime": 86400 // Token 过期时间(分钟)
  472 + }
  473 +}
  474 +```
  475 +
  476 +### Redis 配置(可选)
  477 +
  478 +```json
  479 +{
  480 + "Redis": {
  481 + "IsEnabled": false,
  482 + "Configuration": "127.0.0.1:6379,password=123,defaultDatabase=13",
  483 + "JobDb": 13
  484 + }
  485 +}
  486 +```
  487 +
  488 +### 权限管理配置
  489 +
  490 +```json
  491 +{
  492 + "RbacOptions": {
  493 + "AdminPassword": "123456", // 默认管理员密码
  494 + "EnableCaptcha": false, // 是否启用验证码
  495 + "EnableRegister": false, // 是否允许注册
  496 + "EnableDataBaseBackup": false // 是否启用数据库备份
  497 + }
  498 +}
  499 +```
  500 +
  501 +---
  502 +
  503 +## 📚 更多文档
  504 +
  505 +- [框架快速开始教程](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)
  506 +- [框架功能模块教程](https://ccnetcore.com/article/8c464ab3-8ba5-2761-a4b0-3a0f83a9f312)
  507 +- [实战演练开发教程](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)
  508 +- [社区导航大全](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742/fb8c871b-41fc-21bc-474f-3a154498f42b)
  509 +
  510 +---
  511 +
  512 +## 🎁 内置模块
  513 +
  514 +- ✅ **RBAC 权限管理系统** - 完整的用户权限管理
  515 +- ✅ **租户管理模块** - 多租户支持
  516 +- ✅ **审计日志模块** - 操作日志记录
  517 +- ✅ **设置管理模块** - 系统配置管理
  518 +
  519 +---
  520 +
  521 +## 🌟 特性
  522 +
  523 +- ✅ **开箱即用**:默认 SQLite,无需配置即可运行
  524 +- ✅ **CodeFirst**:自动创建数据库表结构
  525 +- ✅ **种子数据**:自动初始化基础数据
  526 +- ✅ **动态 API**:无需手写 Controller
  527 +- ✅ **CRUD 基类**:快速开发增删改查
  528 +- ✅ **权限控制**:内置完整的权限管理
  529 +- ✅ **多租户**:支持 SaaS 架构
  530 +- ✅ **模块化**:功能模块化,按需引用
  531 +- ✅ **源码开放**:框架源码直接提供,方便学习和二开
  532 +
  533 +---
  534 +
  535 +## 🔧 常见问题
  536 +
  537 +### Q: 如何切换数据库?
  538 +
  539 +A: 修改 `appsettings.json` 中的 `DbConnOptions` 配置,设置 `DbType` 和 `Url`。
  540 +
  541 +### Q: 如何禁用 CodeFirst 自动建表?
  542 +
  543 +A: 设置 `EnabledCodeFirst: false`,然后手动执行 SQL 脚本创建表。
  544 +
  545 +### Q: 如何添加新的业务模块?
  546 +
  547 +A: **所有业务代码都应该在 `module` 目录下创建自己的模块**。参考 `antis-erp` 模块的结构,创建包含 Domain.Shared、Domain、Application.Contracts、Application、SqlSugarCore 五个项目的模块,然后在 `YiAbpWebModule.cs` 中注册模块依赖和动态 API。
  548 +
  549 +### Q: 如何自定义 API 路由?
  550 +
  551 +A: 在服务方法上使用 `[Route("your-route")]` 特性。
  552 +
  553 +### Q: 如何启用多租户?
  554 +
  555 +A: 设置 `EnabledSaasMultiTenancy: true`,并在 Swagger 中使用 `__tenant` 参数指定租户。
  556 +
  557 +---
  558 +
  559 +## 📞 联系我们
  560 +
  561 +- **官网**:[ccnetcore.com](https://ccnetcore.com)
  562 +- **演示地址**:[data.ccnetcore.com:2000](https://data.ccnetcore.com:2000)(账号:cc / 密码:123456)
  563 +- **QQ 群**:981136525(官方五群)
  564 +- **微信**:添加 `chengzilaoge520`(备注:拉群)
  565 +
  566 +---
  567 +
  568 +## 📄 许可证
  569 +
  570 +本项目采用 [MIT](LICENSE) 许可证。
  571 +
  572 +---
  573 +
  574 +## 🙏 致谢
  575 +
  576 +感谢以下开源项目:
  577 +
  578 +- [ABP Framework](https://github.com/abpframework/abp)
  579 +- [SqlSugar](https://www.donet5.com/)
  580 +- [Vben Admin](https://github.com/vbenjs/vben-admin-thin-next)
  581 +
  582 +---
  583 +
  584 +<div align="center">
  585 +
  586 +**⭐ 如果这个项目对你有帮助,请给个 Star ⭐**
  587 +
  588 +Made with ❤️ by [Yi Framework Team](https://ccnetcore.com)
  589 +
  590 +</div>
... ...
Yi.Abp.Net8/.DS_Store 0 → 100644
No preview for this file type
Yi.Abp.Net8/.dockerignore 0 → 100644
  1 +++ a/Yi.Abp.Net8/.dockerignore
  1 +**/.classpath
  2 +**/.dockerignore
  3 +**/.env
  4 +**/.git
  5 +**/.gitignore
  6 +**/.project
  7 +**/.settings
  8 +**/.toolstarget
  9 +**/.vs
  10 +**/.vscode
  11 +**/*.*proj.user
  12 +**/*.dbmdl
  13 +**/*.jfm
  14 +**/azds.yaml
  15 +**/bin
  16 +**/charts
  17 +**/docker-compose*
  18 +**/Dockerfile*
  19 +**/node_modules
  20 +**/npm-debug.log
  21 +**/obj
  22 +**/secrets.dev.yaml
  23 +**/values.dev.yaml
  24 +LICENSE
  25 +README.md
  26 +!**/.gitignore
  27 +!.git/HEAD
  28 +!.git/config
  29 +!.git/packed-refs
  30 +!.git/refs/heads/**
  31 +appsettings.Development.json
  32 +appsettings.Production.json
  33 +appsettings.Staging.json
0 34 \ No newline at end of file
... ...
Yi.Abp.Net8/Dockerfile 0 → 100644
  1 +++ a/Yi.Abp.Net8/Dockerfile
  1 +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
  2 +USER root
  3 +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  4 +RUN echo "Asia/Shanghai" > /etc/timezone
  5 +WORKDIR /app
  6 +EXPOSE 19001
  7 +
  8 +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
  9 +ARG BUILD_CONFIGURATION=Release
  10 +WORKDIR /main
  11 +COPY . .
  12 +WORKDIR "/main/src/Yi.Abp.Web"
  13 +RUN dotnet restore "Yi.Abp.Web.csproj"
  14 +
  15 +FROM build AS publish
  16 +WORKDIR "/main/src/Yi.Abp.Web"
  17 +RUN dotnet publish "Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
  18 +
  19 +FROM base AS final
  20 +WORKDIR /app
  21 +COPY --from=publish /app/publish .
  22 +ENTRYPOINT ["dotnet", "Yi.Abp.Web.dll"]
0 23 \ No newline at end of file
... ...
Yi.Abp.Net8/DockerfileFast 0 → 100644
  1 +++ a/Yi.Abp.Net8/DockerfileFast
  1 +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
  2 +USER root
  3 +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  4 +RUN echo "Asia/Shanghai" > /etc/timezone
  5 +WORKDIR /app
  6 +EXPOSE 19001
  7 +
  8 +FROM base AS final
  9 +WORKDIR /app
  10 +COPY ["./publish","."]
  11 +ENTRYPOINT ["dotnet", "Yi.Abp.Web.dll"]
0 12 \ No newline at end of file
... ...
Yi.Abp.Net8/Yi.Abp.sln 0 → 100644
  1 +++ a/Yi.Abp.Net8/Yi.Abp.sln
  1 +
  2 +Microsoft Visual Studio Solution File, Format Version 12.00
  3 +# Visual Studio Version 17
  4 +VisualStudioVersion = 17.7.34202.233
  5 +MinimumVisualStudioVersion = 10.0.40219.1
  6 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Web", "src\Yi.Abp.Web\Yi.Abp.Web.csproj", "{15913E44-DA92-44B9-9AC5-E9457EA34BF5}"
  7 +EndProject
  8 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore", "framework\Yi.Framework.SqlSugarCore\Yi.Framework.SqlSugarCore.csproj", "{DC431ECC-C75D-4B01-8B79-4861948179BB}"
  9 +EndProject
  10 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B782C78B-6C17-49E6-A237-3383BA720766}"
  11 +EndProject
  12 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "framework", "framework", "{77B949E9-530E-45A5-9657-20F7D5C6875C}"
  13 +EndProject
  14 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "module", "module", "{2317227D-7796-4E7B-BEDB-7CD1CAE7B853}"
  15 +EndProject
  16 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.SqlSugarCore", "src\Yi.Abp.SqlSugarCore\Yi.Abp.SqlSugarCore.csproj", "{9A7BBA40-28D6-4900-9E1D-D627A516EE72}"
  17 +EndProject
  18 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Application", "src\Yi.Abp.Application\Yi.Abp.Application.csproj", "{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}"
  19 +EndProject
  20 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Application.Contracts", "src\Yi.Abp.Application.Contracts\Yi.Abp.Application.Contracts.csproj", "{51EEBF59-3D37-4681-981D-56F8D8F8968D}"
  21 +EndProject
  22 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Domain", "src\Yi.Abp.Domain\Yi.Abp.Domain.csproj", "{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}"
  23 +EndProject
  24 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Domain.Shared", "src\Yi.Abp.Domain.Shared\Yi.Abp.Domain.Shared.csproj", "{F4D5A496-BFBE-470B-A05B-CB5823B47E72}"
  25 +EndProject
  26 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6A5375C6-1D55-4E93-9B19-736F1C68CBC3}"
  27 + ProjectSection(SolutionItems) = preProject
  28 + common.props = common.props
  29 + end.sh = end.sh
  30 + logo.png = logo.png
  31 + start.sh = start.sh
  32 + tool.bat = tool.bat
  33 + usings.props = usings.props
  34 + version.props = version.props
  35 + publish.bat = publish.bat
  36 + publish_Demo.bat = publish_Demo.bat
  37 + Dockerfile = Dockerfile
  38 + DockerfileFast = DockerfileFast
  39 + EndProjectSection
  40 +EndProject
  41 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore.Abstractions", "framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj", "{FD6D6860-3753-4747-8A26-977E4A3001F9}"
  42 +EndProject
  43 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Core", "framework\Yi.Framework.Core\Yi.Framework.Core.csproj", "{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}"
  44 +EndProject
  45 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Mapster", "framework\Yi.Framework.Mapster\Yi.Framework.Mapster.csproj", "{1995A019-C8AE-467E-B427-ED57D6CBF44F}"
  46 +EndProject
  47 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AspNetCore", "framework\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj", "{F5011C0D-209B-4A98-BBE3-68157503EEF8}"
  48 +EndProject
  49 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Ddd.Application.Contracts", "framework\Yi.Framework.Ddd.Application.Contracts\Yi.Framework.Ddd.Application.Contracts.csproj", "{0A8296A3-C11F-4F13-8E49-6BC8188D4804}"
  50 +EndProject
  51 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Ddd.Application", "framework\Yi.Framework.Ddd.Application\Yi.Framework.Ddd.Application.csproj", "{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}"
  52 +EndProject
  53 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rbac", "rbac", "{9CC7A457-1236-40BA-B47B-E7B710A3F061}"
  54 +EndProject
  55 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.Application.Contracts", "module\rbac\Yi.Framework.Rbac.Application.Contracts\Yi.Framework.Rbac.Application.Contracts.csproj", "{1C360956-8CD8-407E-B87F-D0BD57068EB9}"
  56 +EndProject
  57 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.Application", "module\rbac\Yi.Framework.Rbac.Application\Yi.Framework.Rbac.Application.csproj", "{4F02B08D-5FE2-460D-BCA5-DA565151AE30}"
  58 +EndProject
  59 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.Domain", "module\rbac\Yi.Framework.Rbac.Domain\Yi.Framework.Rbac.Domain.csproj", "{C04D3F71-1557-46D0-B810-97B1FBB6AB73}"
  60 +EndProject
  61 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.Domain.Shared", "module\rbac\Yi.Framework.Rbac.Domain.Shared\Yi.Framework.Rbac.Domain.Shared.csproj", "{A2BB899D-4F9A-4184-81BD-94B938E2AB03}"
  62 +EndProject
  63 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.SqlSugarCore", "module\rbac\Yi.Framework.Rbac.SqlSugarCore\Yi.Framework.Rbac.SqlSugarCore.csproj", "{4503A2F9-139D-4CBC-AF11-689C34F0D77B}"
  64 +EndProject
  65 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "audit-logging", "audit-logging", "{73CCF2C4-B9FD-44AB-8D4B-0A421805B094}"
  66 +EndProject
  67 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AuditLogging.SqlSugarCore", "module\audit-logging\Yi.Framework.AuditLogging.SqlSugarCore\Yi.Framework.AuditLogging.SqlSugarCore.csproj", "{48806510-8E18-4E1E-9BAF-5B97E88C5FC3}"
  68 +EndProject
  69 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AspNetCore.Authentication.OAuth", "framework\Yi.Framework.AspNetCore.Authentication.OAuth\Yi.Framework.AspNetCore.Authentication.OAuth.csproj", "{791AC2FA-50D3-4408-8D68-31DA72F608BE}"
  70 +EndProject
  71 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tenant-management", "tenant-management", "{499A8C71-7892-42D0-A77E-48756E1EFF16}"
  72 +EndProject
  73 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.TenantManagement.SqlSugarCore", "module\tenant-management\Yi.Framework.TenantManagement.SqlSugarCore\Yi.Framework.TenantManagement.SqlSugarCore.csproj", "{FA5BBAA1-08DC-472F-BB2C-5314E59D1556}"
  74 +EndProject
  75 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.TenantManagement.Domain", "module\tenant-management\Yi.Framework.TenantManagement.Domain\Yi.Framework.TenantManagement.Domain.csproj", "{54D8E2BC-591C-4344-A58E-874D49C00B41}"
  76 +EndProject
  77 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AuditLogging.Domain", "module\audit-logging\Yi.Framework.AuditLogging.Domain\Yi.Framework.AuditLogging.Domain.csproj", "{EFD13211-17B5-400A-B99A-9F6F4E520C1E}"
  78 +EndProject
  79 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AuditLogging.Domain.Shared", "module\audit-logging\Yi.Framework.AuditLogging.Domain.Shared\Yi.Framework.AuditLogging.Domain.Shared.csproj", "{9C8C3C53-3DCE-4516-867E-228858E61B26}"
  80 +EndProject
  81 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.TenantManagement.Application", "module\tenant-management\Yi.Framework.TenantManagement.Application\Yi.Framework.TenantManagement.Application.csproj", "{17816837-E53B-486B-B796-53C601FE6CD9}"
  82 +EndProject
  83 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.TenantManagement.Application.Contracts", "module\tenant-management\Yi.Framework.TenantManagement.Application.Contracts\Yi.Framework.TenantManagement.Application.Contracts.csproj", "{FA735055-CBDD-4EFD-B84B-85810DA1425E}"
  84 +EndProject
  85 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Caching.FreeRedis", "framework\Yi.Framework.Caching.FreeRedis\Yi.Framework.Caching.FreeRedis.csproj", "{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}"
  86 +EndProject
  87 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "client", "client", "{8B27846A-043D-4F2F-8140-5CEC9D1863B5}"
  88 +EndProject
  89 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.HttpApi.Client", "client\Yi.Abp.HttpApi.Client\Yi.Abp.HttpApi.Client.csproj", "{6B554DCC-3A81-4624-9141-4E39365ADA35}"
  90 +EndProject
  91 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Client.Console", "client\Yi.Abp.Client.Console\Yi.Abp.Client.Console.csproj", "{2D23B44A-DFA3-4C36-8516-4F5AE442403C}"
  92 +EndProject
  93 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Client.WebApi", "client\Yi.Abp.Client.WebApi\Yi.Abp.Client.WebApi.csproj", "{00E49781-C6A0-491C-86A1-46F685C90915}"
  94 +EndProject
  95 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "setting-management", "setting-management", "{8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}"
  96 +EndProject
  97 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SettingManagement.Domain", "module\setting-management\Yi.Framework.SettingManagement.Domain\Yi.Framework.SettingManagement.Domain.csproj", "{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}"
  98 +EndProject
  99 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SettingManagement.SqlSugarCore", "module\setting-management\Yi.Framework.SettingManagement.SqlSugarCore\Yi.Framework.SettingManagement.SqlSugarCore.csproj", "{495C4643-39D4-46E7-BDC8-237589627BE4}"
  100 +EndProject
  101 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.SettingManagement.Application", "module\setting-management\Yi.Framework.SettingManagement.Application\Yi.Framework.SettingManagement.Application.csproj", "{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}"
  102 +EndProject
  103 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.WeChat.MiniProgram", "framework\Yi.Framework.WeChat.MiniProgram\Yi.Framework.WeChat.MiniProgram.csproj", "{81CEA2ED-917B-41D8-BE0D-39A785B050C0}"
  104 +EndProject
  105 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.BackgroundWorkers.Hangfire", "framework\Yi.Framework.BackgroundWorkers.Hangfire\Yi.Framework.BackgroundWorkers.Hangfire.csproj", "{862CA181-BEE6-4870-82D2-B662E527ED8C}"
  106 +EndProject
  107 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "antis-erp", "antis-erp", "{609660A4-AEE6-61D5-25B9-1FDCB688B986}"
  108 +EndProject
  109 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antis.Erp.Application", "module\antis-erp\Antis.Erp.Application\Antis.Erp.Application.csproj", "{151D2C44-2F56-4407-8FBF-B683B23617F1}"
  110 +EndProject
  111 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antis.Erp.Application.Contracts", "module\antis-erp\Antis.Erp.Application.Contracts\Antis.Erp.Application.Contracts.csproj", "{810FE198-77D9-4B2D-A83B-AA43D79B806A}"
  112 +EndProject
  113 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antis.Erp.Domain", "module\antis-erp\Antis.Erp.Domain\Antis.Erp.Domain.csproj", "{B7E56684-FF7B-40C0-9640-DC83CD59AD71}"
  114 +EndProject
  115 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antis.Erp.Domain.Shared", "module\antis-erp\Antis.Erp.Domain.Shared\Antis.Erp.Domain.Shared.csproj", "{867F0C31-9016-4903-ABAA-EB3A0A11953F}"
  116 +EndProject
  117 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antis.Erp.SqlSugarCore", "module\antis-erp\Antis.Erp.SqlSugarCore\Antis.Erp.SqlSugarCore.csproj", "{84973327-BF5A-4A7D-8D86-B9712559AD5A}"
  118 +EndProject
  119 +Global
  120 + GlobalSection(SolutionConfigurationPlatforms) = preSolution
  121 + Debug|Any CPU = Debug|Any CPU
  122 + Debug|x64 = Debug|x64
  123 + Debug|x86 = Debug|x86
  124 + Release|Any CPU = Release|Any CPU
  125 + Release|x64 = Release|x64
  126 + Release|x86 = Release|x86
  127 + EndGlobalSection
  128 + GlobalSection(ProjectConfigurationPlatforms) = postSolution
  129 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  130 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
  131 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|x64.ActiveCfg = Debug|Any CPU
  132 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|x64.Build.0 = Debug|Any CPU
  133 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|x86.ActiveCfg = Debug|Any CPU
  134 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|x86.Build.0 = Debug|Any CPU
  135 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
  136 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|Any CPU.Build.0 = Release|Any CPU
  137 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|x64.ActiveCfg = Release|Any CPU
  138 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|x64.Build.0 = Release|Any CPU
  139 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|x86.ActiveCfg = Release|Any CPU
  140 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|x86.Build.0 = Release|Any CPU
  141 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  142 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
  143 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|x64.ActiveCfg = Debug|Any CPU
  144 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|x64.Build.0 = Debug|Any CPU
  145 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|x86.ActiveCfg = Debug|Any CPU
  146 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|x86.Build.0 = Debug|Any CPU
  147 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
  148 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|Any CPU.Build.0 = Release|Any CPU
  149 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|x64.ActiveCfg = Release|Any CPU
  150 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|x64.Build.0 = Release|Any CPU
  151 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|x86.ActiveCfg = Release|Any CPU
  152 + {DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|x86.Build.0 = Release|Any CPU
  153 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  154 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|Any CPU.Build.0 = Debug|Any CPU
  155 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|x64.ActiveCfg = Debug|Any CPU
  156 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|x64.Build.0 = Debug|Any CPU
  157 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|x86.ActiveCfg = Debug|Any CPU
  158 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|x86.Build.0 = Debug|Any CPU
  159 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|Any CPU.ActiveCfg = Release|Any CPU
  160 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|Any CPU.Build.0 = Release|Any CPU
  161 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|x64.ActiveCfg = Release|Any CPU
  162 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|x64.Build.0 = Release|Any CPU
  163 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|x86.ActiveCfg = Release|Any CPU
  164 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|x86.Build.0 = Release|Any CPU
  165 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  166 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
  167 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|x64.ActiveCfg = Debug|Any CPU
  168 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|x64.Build.0 = Debug|Any CPU
  169 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|x86.ActiveCfg = Debug|Any CPU
  170 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|x86.Build.0 = Debug|Any CPU
  171 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
  172 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|Any CPU.Build.0 = Release|Any CPU
  173 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|x64.ActiveCfg = Release|Any CPU
  174 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|x64.Build.0 = Release|Any CPU
  175 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|x86.ActiveCfg = Release|Any CPU
  176 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|x86.Build.0 = Release|Any CPU
  177 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  178 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|Any CPU.Build.0 = Debug|Any CPU
  179 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|x64.ActiveCfg = Debug|Any CPU
  180 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|x64.Build.0 = Debug|Any CPU
  181 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|x86.ActiveCfg = Debug|Any CPU
  182 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|x86.Build.0 = Debug|Any CPU
  183 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|Any CPU.ActiveCfg = Release|Any CPU
  184 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|Any CPU.Build.0 = Release|Any CPU
  185 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|x64.ActiveCfg = Release|Any CPU
  186 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|x64.Build.0 = Release|Any CPU
  187 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|x86.ActiveCfg = Release|Any CPU
  188 + {51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|x86.Build.0 = Release|Any CPU
  189 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  190 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
  191 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|x64.ActiveCfg = Debug|Any CPU
  192 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|x64.Build.0 = Debug|Any CPU
  193 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|x86.ActiveCfg = Debug|Any CPU
  194 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|x86.Build.0 = Debug|Any CPU
  195 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
  196 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|Any CPU.Build.0 = Release|Any CPU
  197 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|x64.ActiveCfg = Release|Any CPU
  198 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|x64.Build.0 = Release|Any CPU
  199 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|x86.ActiveCfg = Release|Any CPU
  200 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|x86.Build.0 = Release|Any CPU
  201 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  202 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|Any CPU.Build.0 = Debug|Any CPU
  203 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|x64.ActiveCfg = Debug|Any CPU
  204 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|x64.Build.0 = Debug|Any CPU
  205 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|x86.ActiveCfg = Debug|Any CPU
  206 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|x86.Build.0 = Debug|Any CPU
  207 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|Any CPU.ActiveCfg = Release|Any CPU
  208 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|Any CPU.Build.0 = Release|Any CPU
  209 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|x64.ActiveCfg = Release|Any CPU
  210 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|x64.Build.0 = Release|Any CPU
  211 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|x86.ActiveCfg = Release|Any CPU
  212 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|x86.Build.0 = Release|Any CPU
  213 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  214 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
  215 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|x64.ActiveCfg = Debug|Any CPU
  216 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|x64.Build.0 = Debug|Any CPU
  217 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|x86.ActiveCfg = Debug|Any CPU
  218 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|x86.Build.0 = Debug|Any CPU
  219 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
  220 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|Any CPU.Build.0 = Release|Any CPU
  221 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|x64.ActiveCfg = Release|Any CPU
  222 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|x64.Build.0 = Release|Any CPU
  223 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|x86.ActiveCfg = Release|Any CPU
  224 + {FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|x86.Build.0 = Release|Any CPU
  225 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  226 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
  227 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|x64.ActiveCfg = Debug|Any CPU
  228 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|x64.Build.0 = Debug|Any CPU
  229 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|x86.ActiveCfg = Debug|Any CPU
  230 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|x86.Build.0 = Debug|Any CPU
  231 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
  232 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|Any CPU.Build.0 = Release|Any CPU
  233 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|x64.ActiveCfg = Release|Any CPU
  234 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|x64.Build.0 = Release|Any CPU
  235 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|x86.ActiveCfg = Release|Any CPU
  236 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|x86.Build.0 = Release|Any CPU
  237 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  238 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|Any CPU.Build.0 = Debug|Any CPU
  239 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|x64.ActiveCfg = Debug|Any CPU
  240 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|x64.Build.0 = Debug|Any CPU
  241 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|x86.ActiveCfg = Debug|Any CPU
  242 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|x86.Build.0 = Debug|Any CPU
  243 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|Any CPU.ActiveCfg = Release|Any CPU
  244 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|Any CPU.Build.0 = Release|Any CPU
  245 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|x64.ActiveCfg = Release|Any CPU
  246 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|x64.Build.0 = Release|Any CPU
  247 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|x86.ActiveCfg = Release|Any CPU
  248 + {1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|x86.Build.0 = Release|Any CPU
  249 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  250 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
  251 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|x64.ActiveCfg = Debug|Any CPU
  252 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|x64.Build.0 = Debug|Any CPU
  253 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|x86.ActiveCfg = Debug|Any CPU
  254 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|x86.Build.0 = Debug|Any CPU
  255 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
  256 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|Any CPU.Build.0 = Release|Any CPU
  257 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|x64.ActiveCfg = Release|Any CPU
  258 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|x64.Build.0 = Release|Any CPU
  259 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|x86.ActiveCfg = Release|Any CPU
  260 + {F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|x86.Build.0 = Release|Any CPU
  261 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  262 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|Any CPU.Build.0 = Debug|Any CPU
  263 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|x64.ActiveCfg = Debug|Any CPU
  264 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|x64.Build.0 = Debug|Any CPU
  265 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|x86.ActiveCfg = Debug|Any CPU
  266 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|x86.Build.0 = Debug|Any CPU
  267 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|Any CPU.ActiveCfg = Release|Any CPU
  268 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|Any CPU.Build.0 = Release|Any CPU
  269 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|x64.ActiveCfg = Release|Any CPU
  270 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|x64.Build.0 = Release|Any CPU
  271 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|x86.ActiveCfg = Release|Any CPU
  272 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|x86.Build.0 = Release|Any CPU
  273 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  274 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
  275 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|x64.ActiveCfg = Debug|Any CPU
  276 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|x64.Build.0 = Debug|Any CPU
  277 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|x86.ActiveCfg = Debug|Any CPU
  278 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|x86.Build.0 = Debug|Any CPU
  279 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
  280 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|Any CPU.Build.0 = Release|Any CPU
  281 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|x64.ActiveCfg = Release|Any CPU
  282 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|x64.Build.0 = Release|Any CPU
  283 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|x86.ActiveCfg = Release|Any CPU
  284 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|x86.Build.0 = Release|Any CPU
  285 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  286 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
  287 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|x64.ActiveCfg = Debug|Any CPU
  288 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|x64.Build.0 = Debug|Any CPU
  289 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|x86.ActiveCfg = Debug|Any CPU
  290 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|x86.Build.0 = Debug|Any CPU
  291 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
  292 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|Any CPU.Build.0 = Release|Any CPU
  293 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|x64.ActiveCfg = Release|Any CPU
  294 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|x64.Build.0 = Release|Any CPU
  295 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|x86.ActiveCfg = Release|Any CPU
  296 + {1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|x86.Build.0 = Release|Any CPU
  297 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  298 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|Any CPU.Build.0 = Debug|Any CPU
  299 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|x64.ActiveCfg = Debug|Any CPU
  300 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|x64.Build.0 = Debug|Any CPU
  301 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|x86.ActiveCfg = Debug|Any CPU
  302 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|x86.Build.0 = Debug|Any CPU
  303 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|Any CPU.ActiveCfg = Release|Any CPU
  304 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|Any CPU.Build.0 = Release|Any CPU
  305 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|x64.ActiveCfg = Release|Any CPU
  306 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|x64.Build.0 = Release|Any CPU
  307 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|x86.ActiveCfg = Release|Any CPU
  308 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|x86.Build.0 = Release|Any CPU
  309 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  310 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|Any CPU.Build.0 = Debug|Any CPU
  311 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|x64.ActiveCfg = Debug|Any CPU
  312 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|x64.Build.0 = Debug|Any CPU
  313 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|x86.ActiveCfg = Debug|Any CPU
  314 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|x86.Build.0 = Debug|Any CPU
  315 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|Any CPU.ActiveCfg = Release|Any CPU
  316 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|Any CPU.Build.0 = Release|Any CPU
  317 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|x64.ActiveCfg = Release|Any CPU
  318 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|x64.Build.0 = Release|Any CPU
  319 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|x86.ActiveCfg = Release|Any CPU
  320 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|x86.Build.0 = Release|Any CPU
  321 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  322 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|Any CPU.Build.0 = Debug|Any CPU
  323 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|x64.ActiveCfg = Debug|Any CPU
  324 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|x64.Build.0 = Debug|Any CPU
  325 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|x86.ActiveCfg = Debug|Any CPU
  326 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|x86.Build.0 = Debug|Any CPU
  327 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|Any CPU.ActiveCfg = Release|Any CPU
  328 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|Any CPU.Build.0 = Release|Any CPU
  329 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|x64.ActiveCfg = Release|Any CPU
  330 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|x64.Build.0 = Release|Any CPU
  331 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|x86.ActiveCfg = Release|Any CPU
  332 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|x86.Build.0 = Release|Any CPU
  333 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  334 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|Any CPU.Build.0 = Debug|Any CPU
  335 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|x64.ActiveCfg = Debug|Any CPU
  336 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|x64.Build.0 = Debug|Any CPU
  337 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|x86.ActiveCfg = Debug|Any CPU
  338 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|x86.Build.0 = Debug|Any CPU
  339 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|Any CPU.ActiveCfg = Release|Any CPU
  340 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|Any CPU.Build.0 = Release|Any CPU
  341 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|x64.ActiveCfg = Release|Any CPU
  342 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|x64.Build.0 = Release|Any CPU
  343 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|x86.ActiveCfg = Release|Any CPU
  344 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|x86.Build.0 = Release|Any CPU
  345 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  346 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
  347 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|x64.ActiveCfg = Debug|Any CPU
  348 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|x64.Build.0 = Debug|Any CPU
  349 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|x86.ActiveCfg = Debug|Any CPU
  350 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|x86.Build.0 = Debug|Any CPU
  351 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
  352 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|Any CPU.Build.0 = Release|Any CPU
  353 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|x64.ActiveCfg = Release|Any CPU
  354 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|x64.Build.0 = Release|Any CPU
  355 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|x86.ActiveCfg = Release|Any CPU
  356 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|x86.Build.0 = Release|Any CPU
  357 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  358 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
  359 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|x64.ActiveCfg = Debug|Any CPU
  360 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|x64.Build.0 = Debug|Any CPU
  361 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|x86.ActiveCfg = Debug|Any CPU
  362 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|x86.Build.0 = Debug|Any CPU
  363 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
  364 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.Build.0 = Release|Any CPU
  365 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|x64.ActiveCfg = Release|Any CPU
  366 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|x64.Build.0 = Release|Any CPU
  367 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|x86.ActiveCfg = Release|Any CPU
  368 + {791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|x86.Build.0 = Release|Any CPU
  369 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  370 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|Any CPU.Build.0 = Debug|Any CPU
  371 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|x64.ActiveCfg = Debug|Any CPU
  372 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|x64.Build.0 = Debug|Any CPU
  373 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|x86.ActiveCfg = Debug|Any CPU
  374 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|x86.Build.0 = Debug|Any CPU
  375 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|Any CPU.ActiveCfg = Release|Any CPU
  376 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|Any CPU.Build.0 = Release|Any CPU
  377 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|x64.ActiveCfg = Release|Any CPU
  378 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|x64.Build.0 = Release|Any CPU
  379 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|x86.ActiveCfg = Release|Any CPU
  380 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|x86.Build.0 = Release|Any CPU
  381 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  382 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|Any CPU.Build.0 = Debug|Any CPU
  383 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|x64.ActiveCfg = Debug|Any CPU
  384 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|x64.Build.0 = Debug|Any CPU
  385 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|x86.ActiveCfg = Debug|Any CPU
  386 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|x86.Build.0 = Debug|Any CPU
  387 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|Any CPU.ActiveCfg = Release|Any CPU
  388 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|Any CPU.Build.0 = Release|Any CPU
  389 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|x64.ActiveCfg = Release|Any CPU
  390 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|x64.Build.0 = Release|Any CPU
  391 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|x86.ActiveCfg = Release|Any CPU
  392 + {54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|x86.Build.0 = Release|Any CPU
  393 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  394 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
  395 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|x64.ActiveCfg = Debug|Any CPU
  396 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|x64.Build.0 = Debug|Any CPU
  397 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|x86.ActiveCfg = Debug|Any CPU
  398 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|x86.Build.0 = Debug|Any CPU
  399 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
  400 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|Any CPU.Build.0 = Release|Any CPU
  401 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|x64.ActiveCfg = Release|Any CPU
  402 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|x64.Build.0 = Release|Any CPU
  403 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|x86.ActiveCfg = Release|Any CPU
  404 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|x86.Build.0 = Release|Any CPU
  405 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  406 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|Any CPU.Build.0 = Debug|Any CPU
  407 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|x64.ActiveCfg = Debug|Any CPU
  408 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|x64.Build.0 = Debug|Any CPU
  409 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|x86.ActiveCfg = Debug|Any CPU
  410 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|x86.Build.0 = Debug|Any CPU
  411 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|Any CPU.ActiveCfg = Release|Any CPU
  412 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|Any CPU.Build.0 = Release|Any CPU
  413 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|x64.ActiveCfg = Release|Any CPU
  414 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|x64.Build.0 = Release|Any CPU
  415 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|x86.ActiveCfg = Release|Any CPU
  416 + {9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|x86.Build.0 = Release|Any CPU
  417 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  418 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
  419 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|x64.ActiveCfg = Debug|Any CPU
  420 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|x64.Build.0 = Debug|Any CPU
  421 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|x86.ActiveCfg = Debug|Any CPU
  422 + {17816837-E53B-486B-B796-53C601FE6CD9}.Debug|x86.Build.0 = Debug|Any CPU
  423 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
  424 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|Any CPU.Build.0 = Release|Any CPU
  425 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|x64.ActiveCfg = Release|Any CPU
  426 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|x64.Build.0 = Release|Any CPU
  427 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|x86.ActiveCfg = Release|Any CPU
  428 + {17816837-E53B-486B-B796-53C601FE6CD9}.Release|x86.Build.0 = Release|Any CPU
  429 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  430 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|Any CPU.Build.0 = Debug|Any CPU
  431 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|x64.ActiveCfg = Debug|Any CPU
  432 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|x64.Build.0 = Debug|Any CPU
  433 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|x86.ActiveCfg = Debug|Any CPU
  434 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|x86.Build.0 = Debug|Any CPU
  435 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|Any CPU.ActiveCfg = Release|Any CPU
  436 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|Any CPU.Build.0 = Release|Any CPU
  437 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|x64.ActiveCfg = Release|Any CPU
  438 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|x64.Build.0 = Release|Any CPU
  439 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|x86.ActiveCfg = Release|Any CPU
  440 + {FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|x86.Build.0 = Release|Any CPU
  441 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  442 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
  443 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|x64.ActiveCfg = Debug|Any CPU
  444 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|x64.Build.0 = Debug|Any CPU
  445 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|x86.ActiveCfg = Debug|Any CPU
  446 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|x86.Build.0 = Debug|Any CPU
  447 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
  448 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.Build.0 = Release|Any CPU
  449 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|x64.ActiveCfg = Release|Any CPU
  450 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|x64.Build.0 = Release|Any CPU
  451 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|x86.ActiveCfg = Release|Any CPU
  452 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|x86.Build.0 = Release|Any CPU
  453 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  454 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
  455 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|x64.ActiveCfg = Debug|Any CPU
  456 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|x64.Build.0 = Debug|Any CPU
  457 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|x86.ActiveCfg = Debug|Any CPU
  458 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|x86.Build.0 = Debug|Any CPU
  459 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
  460 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|Any CPU.Build.0 = Release|Any CPU
  461 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|x64.ActiveCfg = Release|Any CPU
  462 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|x64.Build.0 = Release|Any CPU
  463 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|x86.ActiveCfg = Release|Any CPU
  464 + {6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|x86.Build.0 = Release|Any CPU
  465 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  466 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|Any CPU.Build.0 = Debug|Any CPU
  467 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|x64.ActiveCfg = Debug|Any CPU
  468 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|x64.Build.0 = Debug|Any CPU
  469 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|x86.ActiveCfg = Debug|Any CPU
  470 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|x86.Build.0 = Debug|Any CPU
  471 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|Any CPU.ActiveCfg = Release|Any CPU
  472 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|Any CPU.Build.0 = Release|Any CPU
  473 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|x64.ActiveCfg = Release|Any CPU
  474 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|x64.Build.0 = Release|Any CPU
  475 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|x86.ActiveCfg = Release|Any CPU
  476 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|x86.Build.0 = Release|Any CPU
  477 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  478 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|Any CPU.Build.0 = Debug|Any CPU
  479 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|x64.ActiveCfg = Debug|Any CPU
  480 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|x64.Build.0 = Debug|Any CPU
  481 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|x86.ActiveCfg = Debug|Any CPU
  482 + {00E49781-C6A0-491C-86A1-46F685C90915}.Debug|x86.Build.0 = Debug|Any CPU
  483 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|Any CPU.ActiveCfg = Release|Any CPU
  484 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|Any CPU.Build.0 = Release|Any CPU
  485 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|x64.ActiveCfg = Release|Any CPU
  486 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|x64.Build.0 = Release|Any CPU
  487 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|x86.ActiveCfg = Release|Any CPU
  488 + {00E49781-C6A0-491C-86A1-46F685C90915}.Release|x86.Build.0 = Release|Any CPU
  489 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  490 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
  491 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|x64.ActiveCfg = Debug|Any CPU
  492 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|x64.Build.0 = Debug|Any CPU
  493 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|x86.ActiveCfg = Debug|Any CPU
  494 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|x86.Build.0 = Debug|Any CPU
  495 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
  496 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|Any CPU.Build.0 = Release|Any CPU
  497 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|x64.ActiveCfg = Release|Any CPU
  498 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|x64.Build.0 = Release|Any CPU
  499 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|x86.ActiveCfg = Release|Any CPU
  500 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|x86.Build.0 = Release|Any CPU
  501 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  502 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
  503 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|x64.ActiveCfg = Debug|Any CPU
  504 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|x64.Build.0 = Debug|Any CPU
  505 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|x86.ActiveCfg = Debug|Any CPU
  506 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|x86.Build.0 = Debug|Any CPU
  507 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
  508 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|Any CPU.Build.0 = Release|Any CPU
  509 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|x64.ActiveCfg = Release|Any CPU
  510 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|x64.Build.0 = Release|Any CPU
  511 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|x86.ActiveCfg = Release|Any CPU
  512 + {495C4643-39D4-46E7-BDC8-237589627BE4}.Release|x86.Build.0 = Release|Any CPU
  513 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  514 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|Any CPU.Build.0 = Debug|Any CPU
  515 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|x64.ActiveCfg = Debug|Any CPU
  516 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|x64.Build.0 = Debug|Any CPU
  517 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|x86.ActiveCfg = Debug|Any CPU
  518 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|x86.Build.0 = Debug|Any CPU
  519 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.ActiveCfg = Release|Any CPU
  520 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.Build.0 = Release|Any CPU
  521 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|x64.ActiveCfg = Release|Any CPU
  522 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|x64.Build.0 = Release|Any CPU
  523 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|x86.ActiveCfg = Release|Any CPU
  524 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|x86.Build.0 = Release|Any CPU
  525 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  526 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
  527 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|x64.ActiveCfg = Debug|Any CPU
  528 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|x64.Build.0 = Debug|Any CPU
  529 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|x86.ActiveCfg = Debug|Any CPU
  530 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|x86.Build.0 = Debug|Any CPU
  531 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
  532 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|Any CPU.Build.0 = Release|Any CPU
  533 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|x64.ActiveCfg = Release|Any CPU
  534 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|x64.Build.0 = Release|Any CPU
  535 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|x86.ActiveCfg = Release|Any CPU
  536 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|x86.Build.0 = Release|Any CPU
  537 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  538 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
  539 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|x64.ActiveCfg = Debug|Any CPU
  540 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|x64.Build.0 = Debug|Any CPU
  541 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|x86.ActiveCfg = Debug|Any CPU
  542 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|x86.Build.0 = Debug|Any CPU
  543 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
  544 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.Build.0 = Release|Any CPU
  545 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|x64.ActiveCfg = Release|Any CPU
  546 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|x64.Build.0 = Release|Any CPU
  547 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|x86.ActiveCfg = Release|Any CPU
  548 + {862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|x86.Build.0 = Release|Any CPU
  549 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  550 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
  551 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|x64.ActiveCfg = Debug|Any CPU
  552 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|x64.Build.0 = Debug|Any CPU
  553 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|x86.ActiveCfg = Debug|Any CPU
  554 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Debug|x86.Build.0 = Debug|Any CPU
  555 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
  556 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|Any CPU.Build.0 = Release|Any CPU
  557 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|x64.ActiveCfg = Release|Any CPU
  558 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|x64.Build.0 = Release|Any CPU
  559 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|x86.ActiveCfg = Release|Any CPU
  560 + {151D2C44-2F56-4407-8FBF-B683B23617F1}.Release|x86.Build.0 = Release|Any CPU
  561 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  562 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|Any CPU.Build.0 = Debug|Any CPU
  563 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|x64.ActiveCfg = Debug|Any CPU
  564 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|x64.Build.0 = Debug|Any CPU
  565 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|x86.ActiveCfg = Debug|Any CPU
  566 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Debug|x86.Build.0 = Debug|Any CPU
  567 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|Any CPU.ActiveCfg = Release|Any CPU
  568 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|Any CPU.Build.0 = Release|Any CPU
  569 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|x64.ActiveCfg = Release|Any CPU
  570 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|x64.Build.0 = Release|Any CPU
  571 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|x86.ActiveCfg = Release|Any CPU
  572 + {810FE198-77D9-4B2D-A83B-AA43D79B806A}.Release|x86.Build.0 = Release|Any CPU
  573 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  574 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|Any CPU.Build.0 = Debug|Any CPU
  575 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|x64.ActiveCfg = Debug|Any CPU
  576 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|x64.Build.0 = Debug|Any CPU
  577 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|x86.ActiveCfg = Debug|Any CPU
  578 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Debug|x86.Build.0 = Debug|Any CPU
  579 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|Any CPU.ActiveCfg = Release|Any CPU
  580 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|Any CPU.Build.0 = Release|Any CPU
  581 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|x64.ActiveCfg = Release|Any CPU
  582 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|x64.Build.0 = Release|Any CPU
  583 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|x86.ActiveCfg = Release|Any CPU
  584 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71}.Release|x86.Build.0 = Release|Any CPU
  585 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  586 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|Any CPU.Build.0 = Debug|Any CPU
  587 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|x64.ActiveCfg = Debug|Any CPU
  588 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|x64.Build.0 = Debug|Any CPU
  589 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|x86.ActiveCfg = Debug|Any CPU
  590 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Debug|x86.Build.0 = Debug|Any CPU
  591 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|Any CPU.ActiveCfg = Release|Any CPU
  592 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|Any CPU.Build.0 = Release|Any CPU
  593 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|x64.ActiveCfg = Release|Any CPU
  594 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|x64.Build.0 = Release|Any CPU
  595 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|x86.ActiveCfg = Release|Any CPU
  596 + {867F0C31-9016-4903-ABAA-EB3A0A11953F}.Release|x86.Build.0 = Release|Any CPU
  597 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  598 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
  599 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|x64.ActiveCfg = Debug|Any CPU
  600 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|x64.Build.0 = Debug|Any CPU
  601 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|x86.ActiveCfg = Debug|Any CPU
  602 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Debug|x86.Build.0 = Debug|Any CPU
  603 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
  604 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|Any CPU.Build.0 = Release|Any CPU
  605 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|x64.ActiveCfg = Release|Any CPU
  606 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|x64.Build.0 = Release|Any CPU
  607 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|x86.ActiveCfg = Release|Any CPU
  608 + {84973327-BF5A-4A7D-8D86-B9712559AD5A}.Release|x86.Build.0 = Release|Any CPU
  609 + EndGlobalSection
  610 + GlobalSection(SolutionProperties) = preSolution
  611 + HideSolutionNode = FALSE
  612 + EndGlobalSection
  613 + GlobalSection(NestedProjects) = preSolution
  614 + {15913E44-DA92-44B9-9AC5-E9457EA34BF5} = {B782C78B-6C17-49E6-A237-3383BA720766}
  615 + {DC431ECC-C75D-4B01-8B79-4861948179BB} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  616 + {9A7BBA40-28D6-4900-9E1D-D627A516EE72} = {B782C78B-6C17-49E6-A237-3383BA720766}
  617 + {746DBBD6-23E8-4D5D-9D23-E2902BE338BD} = {B782C78B-6C17-49E6-A237-3383BA720766}
  618 + {51EEBF59-3D37-4681-981D-56F8D8F8968D} = {B782C78B-6C17-49E6-A237-3383BA720766}
  619 + {7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD} = {B782C78B-6C17-49E6-A237-3383BA720766}
  620 + {F4D5A496-BFBE-470B-A05B-CB5823B47E72} = {B782C78B-6C17-49E6-A237-3383BA720766}
  621 + {FD6D6860-3753-4747-8A26-977E4A3001F9} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  622 + {ECE874D4-F882-4EF4-84A6-A842D9B8FBC5} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  623 + {1995A019-C8AE-467E-B427-ED57D6CBF44F} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  624 + {F5011C0D-209B-4A98-BBE3-68157503EEF8} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  625 + {0A8296A3-C11F-4F13-8E49-6BC8188D4804} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  626 + {F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  627 + {9CC7A457-1236-40BA-B47B-E7B710A3F061} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
  628 + {1C360956-8CD8-407E-B87F-D0BD57068EB9} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
  629 + {4F02B08D-5FE2-460D-BCA5-DA565151AE30} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
  630 + {C04D3F71-1557-46D0-B810-97B1FBB6AB73} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
  631 + {A2BB899D-4F9A-4184-81BD-94B938E2AB03} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
  632 + {4503A2F9-139D-4CBC-AF11-689C34F0D77B} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
  633 + {73CCF2C4-B9FD-44AB-8D4B-0A421805B094} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
  634 + {48806510-8E18-4E1E-9BAF-5B97E88C5FC3} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
  635 + {791AC2FA-50D3-4408-8D68-31DA72F608BE} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  636 + {499A8C71-7892-42D0-A77E-48756E1EFF16} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
  637 + {FA5BBAA1-08DC-472F-BB2C-5314E59D1556} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
  638 + {54D8E2BC-591C-4344-A58E-874D49C00B41} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
  639 + {EFD13211-17B5-400A-B99A-9F6F4E520C1E} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
  640 + {9C8C3C53-3DCE-4516-867E-228858E61B26} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
  641 + {17816837-E53B-486B-B796-53C601FE6CD9} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
  642 + {FA735055-CBDD-4EFD-B84B-85810DA1425E} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
  643 + {862BB0EF-3D4E-44FF-AB15-0EB74CE553D3} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  644 + {6B554DCC-3A81-4624-9141-4E39365ADA35} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
  645 + {2D23B44A-DFA3-4C36-8516-4F5AE442403C} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
  646 + {00E49781-C6A0-491C-86A1-46F685C90915} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
  647 + {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
  648 + {6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
  649 + {495C4643-39D4-46E7-BDC8-237589627BE4} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
  650 + {2A31D7CB-BDCC-4253-BA73-273B6B5E1956} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
  651 + {81CEA2ED-917B-41D8-BE0D-39A785B050C0} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  652 + {862CA181-BEE6-4870-82D2-B662E527ED8C} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
  653 + {609660A4-AEE6-61D5-25B9-1FDCB688B986} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
  654 + {151D2C44-2F56-4407-8FBF-B683B23617F1} = {609660A4-AEE6-61D5-25B9-1FDCB688B986}
  655 + {810FE198-77D9-4B2D-A83B-AA43D79B806A} = {609660A4-AEE6-61D5-25B9-1FDCB688B986}
  656 + {B7E56684-FF7B-40C0-9640-DC83CD59AD71} = {609660A4-AEE6-61D5-25B9-1FDCB688B986}
  657 + {867F0C31-9016-4903-ABAA-EB3A0A11953F} = {609660A4-AEE6-61D5-25B9-1FDCB688B986}
  658 + {84973327-BF5A-4A7D-8D86-B9712559AD5A} = {609660A4-AEE6-61D5-25B9-1FDCB688B986}
  659 + EndGlobalSection
  660 + GlobalSection(ExtensibilityGlobals) = postSolution
  661 + SolutionGuid = {23D6FBC9-C970-4641-BC1E-2AEA59F51C18}
  662 + EndGlobalSection
  663 +EndGlobal
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.Console/Program.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.Console/Program.cs
  1 +using Microsoft.Extensions.DependencyInjection;
  2 +using Microsoft.Extensions.Hosting;
  3 +using Yi.Abp.Client.Console;
  4 +using Yi.Framework.Rbac.Application.Contracts.IServices;
  5 +
  6 +try
  7 +{
  8 + IHost host = Host.CreateDefaultBuilder()
  9 + .ConfigureServices(async (host, service) =>
  10 + {
  11 + await service.AddApplicationAsync<YiAbpClientConsoleModule>();
  12 + })
  13 + .UseAutofac()
  14 + .Build();
  15 +
  16 + //控制台直接调用
  17 + var account = host.Services.GetRequiredService<IAccountService>();
  18 +
  19 + //获取验证码
  20 + var data1 = await account.GetCaptchaImageAsync();
  21 +
  22 + //登录
  23 + var data2 = await account.PostLoginAsync(new Yi.Framework.Rbac.Application.Contracts.Dtos.Account.LoginInputVo { UserName = "cc", Password = "123456", Code = string.Empty, Uuid = string.Empty });
  24 +
  25 +
  26 + host.Run();
  27 +
  28 +}
  29 +catch (Exception ex)
  30 +{
  31 + Console.WriteLine(ex.Message);
  32 + Console.WriteLine(ex.StackTrace);
  33 +}
0 34 \ No newline at end of file
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.Console/Yi.Abp.Client.Console.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.Console/Yi.Abp.Client.Console.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 + <Import Project="..\..\common.props" />
  3 + <PropertyGroup>
  4 + <OutputType>Exe</OutputType>
  5 + <TargetFramework>net8.0</TargetFramework>
  6 + <ImplicitUsings>enable</ImplicitUsings>
  7 + <Nullable>enable</Nullable>
  8 + </PropertyGroup>
  9 + <ItemGroup>
  10 + <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  11 + <PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
  12 + </ItemGroup>
  13 + <ItemGroup>
  14 + <ProjectReference Include="..\Yi.Abp.HttpApi.Client\Yi.Abp.HttpApi.Client.csproj" />
  15 + </ItemGroup>
  16 +</Project>
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.Console/YiAbpClientConsoleModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.Console/YiAbpClientConsoleModule.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +using Volo.Abp.Modularity;
  7 +using Yi.Abp.HttpApi.Client;
  8 +
  9 +namespace Yi.Abp.Client.Console
  10 +{
  11 + [DependsOn(typeof(YiAbpHttpApiClientModule))]
  12 + public class YiAbpClientConsoleModule:AbpModule
  13 + {
  14 + }
  15 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Controllers/AccountController.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Controllers/AccountController.cs
  1 +using Microsoft.AspNetCore.Mvc;
  2 +using Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
  3 +using Yi.Framework.Rbac.Application.Contracts.IServices;
  4 +
  5 +namespace Yi.Abp.Client.WebApi.Controllers
  6 +{
  7 + [ApiController]
  8 + [Route("[controller]")]
  9 + public class AccountController : ControllerBase
  10 + {
  11 +
  12 +
  13 + private readonly ILogger<AccountController> _logger;
  14 + private IAccountService _accountService;
  15 + public AccountController(ILogger<AccountController> logger, IAccountService accountService)
  16 + {
  17 + _logger = logger;
  18 + _accountService = accountService;
  19 + }
  20 +
  21 + [HttpPost("my-login")]
  22 + public async Task<IActionResult> Login(LoginInputVo input)
  23 + {
  24 + await _accountService.PostLoginAsync(input);
  25 + return Ok();
  26 + }
  27 +
  28 +
  29 +
  30 + [HttpGet("my-captcha-image")]
  31 + public async Task<IActionResult> CaptchaImageAsync()
  32 + {
  33 + var output = await _accountService.GetCaptchaImageAsync();
  34 + return Ok(output);
  35 + }
  36 + }
  37 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Program.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Program.cs
  1 +using Autofac.Core;
  2 +using Yi.Abp.Client.WebApi;
  3 +
  4 +var builder = WebApplication.CreateBuilder(args);
  5 +
  6 +// Add services to the container.
  7 +builder.Host.UseAutofac();
  8 +builder.Services.AddControllers();
  9 +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  10 +builder.Services.AddEndpointsApiExplorer();
  11 +builder.Services.AddSwaggerGen();
  12 +await builder.Services.AddApplicationAsync<YiAbpClientWebApiModule>();
  13 +var app = builder.Build();
  14 +
  15 +// Configure the HTTP request pipeline.
  16 +if (app.Environment.IsDevelopment())
  17 +{
  18 + app.UseSwagger();
  19 + app.UseSwaggerUI();
  20 +}
  21 +
  22 +app.UseHttpsRedirection();
  23 +
  24 +app.UseAuthorization();
  25 +
  26 +app.MapControllers();
  27 +
  28 +app.Run();
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Properties/launchSettings.json 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Properties/launchSettings.json
  1 +{
  2 + "$schema": "http://json.schemastore.org/launchsettings.json",
  3 + "iisSettings": {
  4 + "windowsAuthentication": false,
  5 + "anonymousAuthentication": true,
  6 + "iisExpress": {
  7 + "applicationUrl": "http://localhost:35597",
  8 + "sslPort": 44322
  9 + }
  10 + },
  11 + "profiles": {
  12 + "http": {
  13 + "commandName": "Project",
  14 + "dotnetRunMessages": true,
  15 + "launchBrowser": true,
  16 + "launchUrl": "swagger",
  17 + "applicationUrl": "http://localhost:5002",
  18 + "environmentVariables": {
  19 + "ASPNETCORE_ENVIRONMENT": "Development"
  20 + }
  21 + },
  22 + "https": {
  23 + "commandName": "Project",
  24 + "dotnetRunMessages": true,
  25 + "launchBrowser": true,
  26 + "launchUrl": "swagger",
  27 + "applicationUrl": "https://localhost:7108;http://localhost:5002",
  28 + "environmentVariables": {
  29 + "ASPNETCORE_ENVIRONMENT": "Development"
  30 + }
  31 + },
  32 + "IIS Express": {
  33 + "commandName": "IISExpress",
  34 + "launchBrowser": true,
  35 + "launchUrl": "swagger",
  36 + "environmentVariables": {
  37 + "ASPNETCORE_ENVIRONMENT": "Development"
  38 + }
  39 + }
  40 + }
  41 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Yi.Abp.Client.WebApi.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Yi.Abp.Client.WebApi.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk.Web">
  2 + <Import Project="..\..\common.props" />
  3 + <PropertyGroup>
  4 + <TargetFramework>net8.0</TargetFramework>
  5 + <Nullable>enable</Nullable>
  6 + <ImplicitUsings>enable</ImplicitUsings>
  7 + </PropertyGroup>
  8 +
  9 + <ItemGroup>
  10 + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  11 + </ItemGroup>
  12 +
  13 + <ItemGroup>
  14 + <ProjectReference Include="..\Yi.Abp.HttpApi.Client\Yi.Abp.HttpApi.Client.csproj" />
  15 + </ItemGroup>
  16 +
  17 +</Project>
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Yi.Abp.Client.WebApi.http 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/Yi.Abp.Client.WebApi.http
  1 +@Yi.Abp.Client.WebApi_HostAddress = http://localhost:5002
  2 +
  3 +GET {{Yi.Abp.Client.WebApi_HostAddress}}/weatherforecast/
  4 +Accept: application/json
  5 +
  6 +###
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/YiAbpClientWebApiModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/YiAbpClientWebApiModule.cs
  1 +using Volo.Abp.Modularity;
  2 +using Yi.Abp.HttpApi.Client;
  3 +
  4 +namespace Yi.Abp.Client.WebApi
  5 +{
  6 + [DependsOn(typeof(YiAbpHttpApiClientModule))]
  7 + public class YiAbpClientWebApiModule:AbpModule
  8 + {
  9 + }
  10 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/appsettings.Development.json 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/appsettings.Development.json
  1 +{
  2 + "Logging": {
  3 + "LogLevel": {
  4 + "Default": "Information",
  5 + "Microsoft.AspNetCore": "Warning"
  6 + }
  7 + }
  8 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/appsettings.json 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.Client.WebApi/appsettings.json
  1 +{
  2 + "Logging": {
  3 + "LogLevel": {
  4 + "Default": "Information",
  5 + "Microsoft.AspNetCore": "Warning"
  6 + }
  7 + },
  8 + "AllowedHosts": "*"
  9 +}
... ...
Yi.Abp.Net8/client/Yi.Abp.HttpApi.Client/Yi.Abp.HttpApi.Client.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.HttpApi.Client/Yi.Abp.HttpApi.Client.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 + <Import Project="..\..\common.props" />
  3 + <PropertyGroup>
  4 + <TargetFramework>net8.0</TargetFramework>
  5 + <ImplicitUsings>enable</ImplicitUsings>
  6 + <Nullable>enable</Nullable>
  7 + <OutputType>Library</OutputType>
  8 + </PropertyGroup>
  9 +
  10 + <ItemGroup>
  11 + <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  12 + <PackageReference Include="Volo.Abp.Http.Client" Version="$(AbpVersion)" />
  13 + <PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
  14 + </ItemGroup>
  15 +
  16 + <ItemGroup>
  17 + <ProjectReference Include="..\..\src\Yi.Abp.Application.Contracts\Yi.Abp.Application.Contracts.csproj" />
  18 + </ItemGroup>
  19 +
  20 +</Project>
... ...
Yi.Abp.Net8/client/Yi.Abp.HttpApi.Client/YiAbpHttpApiClientModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/client/Yi.Abp.HttpApi.Client/YiAbpHttpApiClientModule.cs
  1 +using Microsoft.Extensions.DependencyInjection;
  2 +using Volo.Abp.Autofac;
  3 +using Volo.Abp.Http.Client;
  4 +using Volo.Abp.Modularity;
  5 +using Yi.Framework.Rbac.Application.Contracts;
  6 +
  7 +namespace Yi.Abp.HttpApi.Client
  8 +{
  9 + [DependsOn(typeof(AbpHttpClientModule),
  10 + typeof(AbpAutofacModule),
  11 +
  12 +
  13 + typeof(YiFrameworkRbacApplicationContractsModule))]
  14 + public class YiAbpHttpApiClientModule : AbpModule
  15 + {
  16 + public override void ConfigureServices(ServiceConfigurationContext context)
  17 + {
  18 + //创建动态客户端代理
  19 + context.Services.AddHttpClientProxies(
  20 + typeof(YiFrameworkRbacApplicationContractsModule).Assembly
  21 +
  22 + );
  23 + Configure<AbpRemoteServiceOptions>(options =>
  24 + {
  25 + options.RemoteServices.Default =
  26 + new RemoteServiceConfiguration("http://localhost:19001");
  27 + });
  28 + }
  29 +
  30 +
  31 + }
  32 +}
... ...
Yi.Abp.Net8/common.props 0 → 100644
  1 +++ a/Yi.Abp.Net8/common.props
  1 +<Project>
  2 + <Import Project="usings.props" />
  3 + <Import Project="version.props" />
  4 +
  5 +
  6 +
  7 +
  8 + <PropertyGroup>
  9 + <TargetFramework>net8.0</TargetFramework>
  10 + <Nullable>enable</Nullable>
  11 + <ImplicitUsings>enable</ImplicitUsings>
  12 + </PropertyGroup>
  13 +
  14 + <PropertyGroup>
  15 + <SatelliteResourceLanguages>en;zh-CN</SatelliteResourceLanguages>
  16 + <LangVersion>latest</LangVersion>
  17 + <Version>1.0.0</Version>
  18 + <NoWarn>$(NoWarn);CS1591;CS8618;CS1998;CS8604;CS8620;CS8600;CS8602</NoWarn>
  19 + <AbpProjectType>app</AbpProjectType>
  20 +
  21 + <PublishDocumentationFiles>true</PublishDocumentationFiles>
  22 + <GenerateDocumentationFile>true</GenerateDocumentationFile>
  23 +
  24 + </PropertyGroup>
  25 +
  26 + <Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile">
  27 + <PropertyGroup>
  28 + <NoWarn>$(NoWarn);0436</NoWarn>
  29 + </PropertyGroup>
  30 + </Target>
  31 +
  32 + <ItemGroup>
  33 + <Content Remove="$(UserProfile)\.nuget\packages\*\*\contentFiles\any\*\*.abppkg*.json" />
  34 + </ItemGroup>
  35 +
  36 + <ItemGroup>
  37 + <FrameworkReference Include="Microsoft.AspNetCore.App" />
  38 + </ItemGroup>
  39 +
  40 +</Project>
0 41 \ No newline at end of file
... ...
Yi.Abp.Net8/end.sh 0 → 100644
  1 +++ a/Yi.Abp.Net8/end.sh
  1 +#!/bin/bash
  2 +kill -9 $(lsof -t -i:19001)
  3 +echo "Yi-进程已关闭"
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthenticationConstants.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthenticationConstants.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth
  8 +{
  9 + public class AuthenticationConstants
  10 + {
  11 + public const string OpenId = "urn:openid";
  12 + public const string AccessToken = "urn:access_token";
  13 + public const string Name = "urn:name";
  14 +
  15 + }
  16 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthenticationOAuthOptions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthenticationOAuthOptions.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +using Microsoft.AspNetCore.Authentication.OAuth;
  7 +
  8 +namespace Yi.Framework.AspNetCore.Authentication.OAuth
  9 +{
  10 + public class AuthenticationOAuthOptions:OAuthOptions
  11 + {
  12 +
  13 + public string RedirectUri { get; set; }
  14 + }
  15 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthticationErrCodeModel.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/AuthticationErrCodeModel.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +using Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
  7 +
  8 +namespace Yi.Framework.AspNetCore.Authentication.OAuth
  9 +{
  10 + public class AuthticationErrCodeModel
  11 + {
  12 + public string error { get; set; }
  13 +
  14 + public string error_description { get; set; }
  15 +
  16 + public static void VerifyErrResponse(string content)
  17 + {
  18 +
  19 + var model = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthticationErrCodeModel>(content);
  20 + if (model.error != null)
  21 + {
  22 +
  23 + throw new Exception($"第三方授权返回错误,错误码:【{model.error}】,错误详情:【{model.error_description}】");
  24 + }
  25 + }
  26 + }
  27 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationConstants.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationConstants.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
  8 +
  9 +/// <summary>
  10 +/// Contains constants specific to the <see cref="GiteeAuthenticationHandler"/>.
  11 +/// </summary>
  12 +public static class GiteeAuthenticationConstants
  13 +{
  14 + public static class Claims
  15 + {
  16 + public const string Url = "urn:gitee:url";
  17 + public const string AvatarUrl = "urn:gitee:avatarUrl";
  18 + }
  19 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationDefaults.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationDefaults.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
  8 +
  9 +/// <summary>
  10 +/// Default values used by the Gitee authentication middleware.
  11 +/// </summary>
  12 +public static class GiteeAuthenticationDefaults
  13 +{
  14 + /// <summary>
  15 + /// Default value for <see cref="AuthenticationScheme.Name"/>.
  16 + /// </summary>
  17 + public const string AuthenticationScheme = "Gitee";
  18 +
  19 + /// <summary>
  20 + /// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
  21 + /// </summary>
  22 + public static readonly string DisplayName = "Gitee";
  23 +
  24 + /// <summary>
  25 + /// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
  26 + /// </summary>
  27 + public static readonly string Issuer = "Gitee";
  28 +
  29 + /// <summary>
  30 + /// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
  31 + /// </summary>
  32 + public static readonly string CallbackPath = "/signin-gitee";
  33 +
  34 + /// <summary>
  35 + /// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
  36 + /// </summary>
  37 + public static readonly string AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
  38 +
  39 + /// <summary>
  40 + /// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
  41 + /// </summary>
  42 + public static readonly string TokenEndpoint = "https://gitee.com/oauth/token";
  43 +
  44 + /// <summary>
  45 + /// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
  46 + /// </summary>
  47 + public static readonly string UserInformationEndpoint = "https://gitee.com/api/v5/user";
  48 +
  49 + /// <summary>
  50 + /// Default value for <see cref="GiteeAuthenticationOptions.UserEmailsEndpoint"/>.
  51 + /// </summary>
  52 + public static readonly string UserEmailsEndpoint = "https://gitee.com/api/v5/emails";
  53 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationExtensions.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +using JetBrains.Annotations;
  8 +using Microsoft.AspNetCore.Authentication;
  9 +
  10 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
  11 +
  12 +/// <summary>
  13 +/// Extension methods to add Gitee authentication capabilities to an HTTP application pipeline.
  14 +/// </summary>
  15 +public static class GiteeAuthenticationExtensions
  16 +{
  17 + /// <summary>
  18 + /// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
  19 + /// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
  20 + /// </summary>
  21 + /// <param name="builder">The authentication builder.</param>
  22 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  23 + public static AuthenticationBuilder AddGitee([NotNull] this AuthenticationBuilder builder)
  24 + {
  25 + return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, options => { });
  26 + }
  27 +
  28 + /// <summary>
  29 + /// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
  30 + /// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
  31 + /// </summary>
  32 + /// <param name="builder">The authentication builder.</param>
  33 + /// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
  34 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  35 + public static AuthenticationBuilder AddGitee(
  36 + [NotNull] this AuthenticationBuilder builder,
  37 + [NotNull] Action<GiteeAuthenticationOptions> configuration)
  38 + {
  39 + return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, configuration);
  40 + }
  41 +
  42 + /// <summary>
  43 + /// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
  44 + /// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
  45 + /// </summary>
  46 + /// <param name="builder">The authentication builder.</param>
  47 + /// <param name="scheme">The authentication scheme associated with this instance.</param>
  48 + /// <param name="configuration">The delegate used to configure the Gitee options.</param>
  49 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  50 + public static AuthenticationBuilder AddGitee(
  51 + [NotNull] this AuthenticationBuilder builder,
  52 + [NotNull] string scheme,
  53 + [NotNull] Action<GiteeAuthenticationOptions> configuration)
  54 + {
  55 + return builder.AddGitee(scheme, GiteeAuthenticationDefaults.DisplayName, configuration);
  56 + }
  57 +
  58 + /// <summary>
  59 + /// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
  60 + /// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
  61 + /// </summary>
  62 + /// <param name="builder">The authentication builder.</param>
  63 + /// <param name="scheme">The authentication scheme associated with this instance.</param>
  64 + /// <param name="caption">The optional display name associated with this instance.</param>
  65 + /// <param name="configuration">The delegate used to configure the Gitee options.</param>
  66 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  67 + public static AuthenticationBuilder AddGitee(
  68 + [NotNull] this AuthenticationBuilder builder,
  69 + [NotNull] string scheme,
  70 + [CanBeNull] string caption,
  71 + [NotNull] Action<GiteeAuthenticationOptions> configuration)
  72 + {
  73 + return builder.AddScheme<GiteeAuthenticationOptions, GiteeAuthenticationHandler>(scheme, caption, configuration);
  74 + }
  75 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationHandler.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationHandler.cs
  1 +using System.Security.Claims;
  2 +using System.Text.Encodings.Web;
  3 +using Microsoft.Extensions.Logging;
  4 +using Microsoft.Extensions.Options;
  5 +using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
  8 +{
  9 + public class GiteeAuthenticationHandler : OauthAuthenticationHandler<GiteeAuthenticationOptions>
  10 + {
  11 + public GiteeAuthenticationHandler(IOptionsMonitor<GiteeAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
  12 + {
  13 + }
  14 +
  15 + public override string AuthenticationSchemeNmae => GiteeAuthenticationDefaults.AuthenticationScheme;
  16 +
  17 + protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
  18 + {
  19 + //获取 accessToken
  20 + var tokenQueryKv = new List<KeyValuePair<string, string?>>()
  21 + {
  22 + new KeyValuePair<string, string?>("grant_type","authorization_code"),
  23 + new KeyValuePair<string, string?>("client_id",Options.ClientId),
  24 + new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
  25 + new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
  26 + new KeyValuePair<string, string?>("code",code)
  27 + };
  28 + var tokenModel = await SendHttpRequestAsync<GiteeAuthticationcationTokenResponse>(GiteeAuthenticationDefaults.TokenEndpoint, tokenQueryKv,HttpMethod.Post);
  29 +
  30 + //获取 userInfo
  31 + var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
  32 + {
  33 + new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
  34 + };
  35 + var userInfoMdoel = await SendHttpRequestAsync<GiteeAuthticationcationUserInfoResponse>(GiteeAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
  36 +
  37 + List<Claim> claims = new List<Claim>()
  38 + {
  39 + new Claim(Claims.AvatarUrl, userInfoMdoel.avatar_url),
  40 + new Claim(Claims.Url, userInfoMdoel.url),
  41 +
  42 + new Claim(AuthenticationConstants.OpenId,userInfoMdoel.id.ToString()),
  43 + new Claim(AuthenticationConstants.Name, userInfoMdoel.name),
  44 + new Claim(AuthenticationConstants.AccessToken, tokenModel.access_token)
  45 + };
  46 + return claims;
  47 + }
  48 + }
  49 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationOptions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthenticationOptions.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +using System.Security.Claims;
  8 +using Microsoft.AspNetCore.Authentication;
  9 +using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
  10 +
  11 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
  12 +
  13 +/// <summary>
  14 +/// Defines a set of options used by <see cref="GiteeAuthenticationHandler"/>.
  15 +/// </summary>
  16 +public class GiteeAuthenticationOptions : AuthenticationOAuthOptions
  17 +{
  18 + public GiteeAuthenticationOptions()
  19 + {
  20 + ClaimsIssuer = GiteeAuthenticationDefaults.Issuer;
  21 +
  22 + CallbackPath = GiteeAuthenticationDefaults.CallbackPath;
  23 +
  24 + AuthorizationEndpoint = GiteeAuthenticationDefaults.AuthorizationEndpoint;
  25 + TokenEndpoint = GiteeAuthenticationDefaults.TokenEndpoint;
  26 + UserInformationEndpoint = GiteeAuthenticationDefaults.UserInformationEndpoint;
  27 + UserEmailsEndpoint = GiteeAuthenticationDefaults.UserEmailsEndpoint;
  28 +
  29 + Scope.Add("user_info");
  30 + Scope.Add("emails");
  31 +
  32 + ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
  33 + ClaimActions.MapJsonKey(ClaimTypes.Name, "login");
  34 + ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
  35 + ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
  36 + ClaimActions.MapJsonKey(Claims.Url, "url");
  37 + }
  38 +
  39 + /// <summary>
  40 + /// Gets or sets the address of the endpoint exposing
  41 + /// the email addresses associated with the logged in user.
  42 + /// </summary>
  43 + public string UserEmailsEndpoint { get; set; }
  44 +}
0 45 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthticationcationHttpModel.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Gitee/GiteeAuthticationcationHttpModel.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
  8 +{
  9 + public class GiteeAuthticationcationTokenResponse
  10 + {
  11 + public string access_token { get; set; }
  12 + public string token_type { get; set; }
  13 + public int expires_in { get; set; }
  14 + public string refresh_token { get; set; }
  15 + public string scope { get; set; }
  16 + public long created_at { get; set; }
  17 + }
  18 +
  19 +
  20 + public class GiteeAuthticationcationOpenIdResponse
  21 + {
  22 + public string client_id { get; set; }
  23 +
  24 + public string openid { get; set; }
  25 +
  26 + }
  27 +
  28 + public class GiteeAuthticationcationUserInfoResponse
  29 + {
  30 + /// <summary>
  31 + /// 也可以等于openId
  32 + /// </summary>
  33 + public int id { get; set; }
  34 + public string login { get; set; }
  35 + public string name { get; set; }
  36 + public string avatar_url { get; set; }
  37 + public string url { get; set; }
  38 + public string html_url { get; set; }
  39 + public string remark { get; set; }
  40 + public string followers_url { get; set; }
  41 + public string following_url { get; set; }
  42 + public string gists_url { get; set; }
  43 + public string starred_url { get; set; }
  44 + public string subscriptions_url { get; set; }
  45 + public string organizations_url { get; set; }
  46 + public string repos_url { get; set; }
  47 + public string events_url { get; set; }
  48 + public string received_events_url { get; set; }
  49 + public string type { get; set; }
  50 + public string blog { get; set; }
  51 + public string weibo { get; set; }
  52 + public string bio { get; set; }
  53 + public int public_repos { get; set; }
  54 + public int public_gists { get; set; }
  55 + public int followers { get; set; }
  56 + public int following { get; set; }
  57 + public int stared { get; set; }
  58 + public int watched { get; set; }
  59 + public DateTime created_at { get; set; }
  60 + public DateTime updated_at { get; set; }
  61 + public string email { get; set; }
  62 + }
  63 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/OAuthAuthenticationHandler.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/OAuthAuthenticationHandler.cs
  1 +using System.Security.Claims;
  2 +using System.Text.Encodings.Web;
  3 +using Microsoft.AspNetCore.Authentication;
  4 +using Microsoft.AspNetCore.WebUtilities;
  5 +using Microsoft.Extensions.Logging;
  6 +using Microsoft.Extensions.Options;
  7 +
  8 +namespace Yi.Framework.AspNetCore.Authentication.OAuth
  9 +{
  10 + public abstract class OauthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
  11 + {
  12 + public abstract string AuthenticationSchemeNmae { get; }
  13 + private AuthenticationScheme _scheme;
  14 +
  15 + public OauthAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder)
  16 + {
  17 + HttpClientFactory = httpClientFactory;
  18 + HttpClient = HttpClientFactory.CreateClient();
  19 + }
  20 +
  21 +
  22 + protected IHttpClientFactory HttpClientFactory { get; }
  23 +
  24 + protected HttpClient HttpClient { get; }
  25 +
  26 +
  27 +
  28 + /// <summary>
  29 + /// 生成认证票据
  30 + /// </summary>
  31 + /// <returns></returns>
  32 + private AuthenticationTicket TicketConver(List<Claim> claims)
  33 + {
  34 + var claimsIdentity = new ClaimsIdentity(claims.ToArray(), AuthenticationSchemeNmae);
  35 + var principal = new ClaimsPrincipal(claimsIdentity);
  36 + return new AuthenticationTicket(principal, AuthenticationSchemeNmae);
  37 + }
  38 +
  39 + protected async Task<HttpModel> SendHttpRequestAsync<HttpModel>(string url, IEnumerable<KeyValuePair<string, string?>> query, HttpMethod? httpMethod = null)
  40 + {
  41 + httpMethod = httpMethod ?? HttpMethod.Get;
  42 +
  43 + var queryUrl = QueryHelpers.AddQueryString(url, query);
  44 + HttpResponseMessage response = null;
  45 + if (httpMethod == HttpMethod.Get)
  46 + {
  47 + response = await HttpClient.GetAsync(queryUrl);
  48 + }
  49 + else if (httpMethod == HttpMethod.Post)
  50 + {
  51 + response = await HttpClient.PostAsync(queryUrl, null);
  52 + }
  53 +
  54 + var content = await response.Content.ReadAsStringAsync();
  55 + if (!response.IsSuccessStatusCode)
  56 + {
  57 + throw new Exception($"授权服务器请求错误,请求地址:{queryUrl},错误信息:{content}");
  58 + }
  59 + VerifyErrResponse(content);
  60 + var model = Newtonsoft.Json.JsonConvert.DeserializeObject<HttpModel>(content);
  61 + return model!;
  62 + }
  63 +
  64 + protected virtual void VerifyErrResponse(string content)
  65 + {
  66 + AuthticationErrCodeModel.VerifyErrResponse(content);
  67 + }
  68 +
  69 + protected abstract Task<List<Claim>> GetAuthTicketAsync(string code);
  70 +
  71 +
  72 + protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
  73 + {
  74 + if (!Context.Request.Query.ContainsKey("code"))
  75 + {
  76 + return AuthenticateResult.Fail("回调未包含code参数");
  77 + }
  78 + var code = Context.Request.Query["code"].ToString();
  79 +
  80 + List<Claim> authTicket = null;
  81 + try
  82 + {
  83 + authTicket = await GetAuthTicketAsync(code);
  84 + }
  85 + catch (Exception ex)
  86 + {
  87 + return AuthenticateResult.Fail(ex.Message ?? "未知错误");
  88 + }
  89 + //成功
  90 + var result = AuthenticateResult.Success(TicketConver(authTicket));
  91 + return result;
  92 + }
  93 + }
  94 +}
  95 +
  96 +
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationConstants.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationConstants.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
  8 +
  9 +/// <summary>
  10 +/// Contains constants specific to the <see cref="QQAuthenticationHandler"/>.
  11 +/// </summary>
  12 +public static class QQAuthenticationConstants
  13 +{
  14 + public static class Claims
  15 + {
  16 + public const string AvatarFullUrl = "urn:qq:avatar_full";
  17 + public const string AvatarUrl = "urn:qq:avatar";
  18 + public const string PictureFullUrl = "urn:qq:picture_full";
  19 + public const string PictureMediumUrl = "urn:qq:picture_medium";
  20 + public const string PictureUrl = "urn:qq:picture";
  21 + public const string UnionId = "urn:qq:unionid";
  22 + }
  23 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationDefaults.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationDefaults.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
  8 +
  9 +/// <summary>
  10 +/// Default values for QQ authentication.
  11 +/// </summary>
  12 +public static class QQAuthenticationDefaults
  13 +{
  14 + /// <summary>
  15 + /// Default value for <see cref="AuthenticationScheme.Name"/>.
  16 + /// </summary>
  17 + public const string AuthenticationScheme = "QQ";
  18 +
  19 + /// <summary>
  20 + /// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
  21 + /// </summary>
  22 + public static readonly string DisplayName = "QQ";
  23 +
  24 + /// <summary>
  25 + /// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
  26 + /// </summary>
  27 + public static readonly string Issuer = "QQ";
  28 +
  29 + /// <summary>
  30 + /// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
  31 + /// </summary>
  32 + public static readonly string CallbackPath = "/signin-qq";
  33 +
  34 + /// <summary>
  35 + /// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
  36 + /// </summary>
  37 + public static readonly string AuthorizationEndpoint = "https://graph.qq.com/oauth2.0/authorize";
  38 +
  39 + /// <summary>
  40 + /// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
  41 + /// </summary>
  42 + public static readonly string TokenEndpoint = "https://graph.qq.com/oauth2.0/token";
  43 +
  44 + /// <summary>
  45 + /// Default value for <see cref="QQAuthenticationOptions.UserIdentificationEndpoint"/>.
  46 + /// </summary>
  47 + public static readonly string UserIdentificationEndpoint = "https://graph.qq.com/oauth2.0/me";
  48 +
  49 + /// <summary>
  50 + /// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
  51 + /// </summary>
  52 + public static readonly string UserInformationEndpoint = "https://graph.qq.com/user/get_user_info";
  53 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationExtensions.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +using JetBrains.Annotations;
  8 +using Microsoft.AspNetCore.Authentication;
  9 +using Microsoft.Extensions.DependencyInjection;
  10 +
  11 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
  12 +
  13 +/// <summary>
  14 +/// Extension methods to add QQ authentication capabilities to an HTTP application pipeline.
  15 +/// </summary>
  16 +public static class QQAuthenticationExtensions
  17 +{
  18 + /// <summary>
  19 + /// Adds <see cref="QQAuthenticationHandler"/> to the specified
  20 + /// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
  21 + /// </summary>
  22 + /// <param name="builder">The authentication builder.</param>
  23 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  24 + public static AuthenticationBuilder AddQQ([NotNull] this AuthenticationBuilder builder)
  25 + {
  26 + return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, options => { });
  27 + }
  28 +
  29 + /// <summary>
  30 + /// Adds <see cref="QQAuthenticationHandler"/> to the specified
  31 + /// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
  32 + /// </summary>
  33 + /// <param name="builder">The authentication builder.</param>
  34 + /// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
  35 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  36 + public static AuthenticationBuilder AddQQ(
  37 + [NotNull] this AuthenticationBuilder builder,
  38 + [NotNull] Action<QQAuthenticationOptions> configuration)
  39 + {
  40 + return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, configuration);
  41 + }
  42 +
  43 + /// <summary>
  44 + /// Adds <see cref="QQAuthenticationHandler"/> to the specified
  45 + /// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
  46 + /// </summary>
  47 + /// <param name="builder">The authentication builder.</param>
  48 + /// <param name="scheme">The authentication scheme associated with this instance.</param>
  49 + /// <param name="configuration">The delegate used to configure the QQ options.</param>
  50 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  51 + public static AuthenticationBuilder AddQQ(
  52 + [NotNull] this AuthenticationBuilder builder,
  53 + [NotNull] string scheme,
  54 + [NotNull] Action<QQAuthenticationOptions> configuration)
  55 + {
  56 + return builder.AddQQ(scheme, QQAuthenticationDefaults.DisplayName, configuration);
  57 + }
  58 +
  59 + /// <summary>
  60 + /// Adds <see cref="QQAuthenticationHandler"/> to the specified
  61 + /// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
  62 + /// </summary>
  63 + /// <param name="builder">The authentication builder.</param>
  64 + /// <param name="scheme">The authentication scheme associated with this instance.</param>
  65 + /// <param name="caption">The optional display name associated with this instance.</param>
  66 + /// <param name="configuration">The delegate used to configure the QQ options.</param>
  67 + /// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
  68 + public static AuthenticationBuilder AddQQ(
  69 + [NotNull] this AuthenticationBuilder builder,
  70 + [NotNull] string scheme,
  71 + [CanBeNull] string caption,
  72 + [NotNull] Action<QQAuthenticationOptions> configuration)
  73 + {
  74 + return builder.AddScheme<QQAuthenticationOptions, QQAuthenticationHandler>(scheme, caption, configuration);
  75 +
  76 + }
  77 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationHandler.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationHandler.cs
  1 +using System.Security.Claims;
  2 +using System.Text.Encodings.Web;
  3 +using Microsoft.Extensions.Logging;
  4 +using Microsoft.Extensions.Options;
  5 +using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
  8 +{
  9 + public class QQAuthenticationHandler : OauthAuthenticationHandler<QQAuthenticationOptions>
  10 + {
  11 + public QQAuthenticationHandler(IOptionsMonitor<QQAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
  12 + {
  13 + }
  14 +
  15 + public override string AuthenticationSchemeNmae => QQAuthenticationDefaults.AuthenticationScheme;
  16 +
  17 + protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
  18 + {
  19 +
  20 + //获取 accessToken
  21 + var tokenQueryKv = new List<KeyValuePair<string, string?>>()
  22 + {
  23 + new KeyValuePair<string, string?>("grant_type","authorization_code"),
  24 + new KeyValuePair<string, string?>("client_id",Options.ClientId),
  25 + new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
  26 + new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
  27 + new KeyValuePair<string, string?>("fmt","json"),
  28 + new KeyValuePair<string, string?>("need_openid","1"),
  29 + new KeyValuePair<string, string?>("code",code)
  30 + };
  31 + var tokenModel = await SendHttpRequestAsync<QQAuthticationcationTokenResponse>(QQAuthenticationDefaults.TokenEndpoint, tokenQueryKv);
  32 +
  33 +
  34 +
  35 + //获取 userInfo
  36 + var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
  37 + {
  38 + new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
  39 + new KeyValuePair<string, string?>("oauth_consumer_key",Options.ClientId),
  40 + new KeyValuePair<string, string?>("openid",tokenModel.openid),
  41 + };
  42 +
  43 + var userInfoMdoel = await SendHttpRequestAsync<QQAuthticationcationUserInfoResponse>(QQAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
  44 +
  45 +
  46 + List<Claim> claims = new List<Claim>()
  47 + {
  48 +
  49 + new Claim(Claims.AvatarFullUrl, userInfoMdoel.figureurl_qq_2),
  50 + new Claim(Claims.AvatarUrl, userInfoMdoel.figureurl_qq_1),
  51 + new Claim(Claims.PictureFullUrl, userInfoMdoel.figureurl_2),
  52 + new Claim(Claims.PictureMediumUrl, userInfoMdoel.figureurl_qq_1),
  53 + new Claim(Claims.PictureUrl, userInfoMdoel.figureurl),
  54 +
  55 + new Claim(AuthenticationConstants.OpenId, tokenModel.openid),
  56 + new Claim(AuthenticationConstants.Name, userInfoMdoel.nickname),
  57 + new Claim(AuthenticationConstants.AccessToken, tokenModel.access_token),
  58 +
  59 + };
  60 + return claims;
  61 +
  62 + }
  63 + }
  64 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationOptions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthenticationOptions.cs
  1 +/*
  2 + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
  3 + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
  4 + * for more information concerning the license and the contributors participating to this project.
  5 + */
  6 +
  7 +using System.Security.Claims;
  8 +using Microsoft.AspNetCore.Authentication;
  9 +using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
  10 +
  11 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
  12 +
  13 +/// <summary>
  14 +/// Defines a set of options used by <see cref="QQAuthenticationHandler"/>.
  15 +/// </summary>
  16 +public class QQAuthenticationOptions : AuthenticationOAuthOptions
  17 +{
  18 + public QQAuthenticationOptions()
  19 + {
  20 + ClaimsIssuer = QQAuthenticationDefaults.Issuer;
  21 + CallbackPath = QQAuthenticationDefaults.CallbackPath;
  22 +
  23 + AuthorizationEndpoint = QQAuthenticationDefaults.AuthorizationEndpoint;
  24 + TokenEndpoint = QQAuthenticationDefaults.TokenEndpoint;
  25 + UserIdentificationEndpoint = QQAuthenticationDefaults.UserIdentificationEndpoint;
  26 + UserInformationEndpoint = QQAuthenticationDefaults.UserInformationEndpoint;
  27 +
  28 + Scope.Add("get_user_info");
  29 +
  30 + ClaimActions.MapJsonKey(ClaimTypes.Name, "nickname");
  31 + ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
  32 + ClaimActions.MapJsonKey(Claims.PictureUrl, "figureurl");
  33 + ClaimActions.MapJsonKey(Claims.PictureMediumUrl, "figureurl_1");
  34 + ClaimActions.MapJsonKey(Claims.PictureFullUrl, "figureurl_2");
  35 + ClaimActions.MapJsonKey(Claims.AvatarUrl, "figureurl_qq_1");
  36 + ClaimActions.MapJsonKey(Claims.AvatarFullUrl, "figureurl_qq_2");
  37 + }
  38 +
  39 + /// <summary>
  40 + /// Gets or sets if the union Id (the primary key of an owner for different apps of the QQ platform) should be put into the user claims.
  41 + /// </summary>
  42 + public bool ApplyForUnionId { get; set; }
  43 +
  44 + /// <summary>
  45 + /// Gets or sets the URL of the user identification endpoint (a.k.a. the "OpenID endpoint").
  46 + /// </summary>
  47 + public string UserIdentificationEndpoint { get; set; }
  48 +
  49 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthticationcationHttpModel.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/QQ/QQAuthticationcationHttpModel.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
  8 +{
  9 + public class QQAuthticationcationTokenResponse
  10 + {
  11 + public string access_token { get; set; }
  12 +
  13 + public string expires_in { get; set; }
  14 +
  15 + public string refresh_token { get; set; }
  16 +
  17 + public string openid { get; set; }
  18 + }
  19 +
  20 +
  21 + public class QQAuthticationcationOpenIdResponse
  22 + {
  23 + public string client_id { get; set; }
  24 +
  25 + public string openid { get; set; }
  26 +
  27 + }
  28 +
  29 + public class QQAuthticationcationUserInfoResponse
  30 + {
  31 + // 返回码
  32 + public int ret { get; set; }
  33 +
  34 + // 如果ret<0,会有相应的错误信息提示
  35 + // 返回数据全部用UTF-8编码
  36 + public string msg { get; set; }
  37 +
  38 + // 判断是否有数据丢失
  39 + // 0或者不返回:没有数据丢失,可以缓存
  40 + // 1:有部分数据丢失或错误,不要缓存
  41 + public int is_lost { get; set; }
  42 +
  43 + // 用户在QQ空间的昵称
  44 + public string nickname { get; set; }
  45 +
  46 + // 大小为30x30像素的QQ空间头像URL
  47 + public string figureurl { get; set; }
  48 +
  49 + // 大小为50x50像素的QQ空间头像URL
  50 + public string figureurl_1 { get; set; }
  51 +
  52 + // 大小为100x100像素的QQ空间头像URL
  53 + public string figureurl_2 { get; set; }
  54 +
  55 + // 大小为40x40像素的QQ头像URL
  56 + public string figureurl_qq_1 { get; set; }
  57 +
  58 + // 大小为100x100像素的QQ头像URL
  59 + // 需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有
  60 + public string figureurl_qq_2 { get; set; }
  61 +
  62 + // 性别。如果获取不到则默认返回"男"
  63 + public string gender { get; set; }
  64 +
  65 + // 性别类型。默认返回2
  66 + public int gender_type { get; set; }
  67 +
  68 + // 省
  69 + public string province { get; set; }
  70 +
  71 + // 市
  72 + public string city { get; set; }
  73 +
  74 + // 年
  75 + public int year { get; set; }
  76 +
  77 + // 星座
  78 + public string constellation { get; set; }
  79 +
  80 + // 标识用户是否为黄钻用户
  81 + public int is_yellow_vip { get; set; }
  82 +
  83 + // 黄钻等级
  84 + public int yellow_vip_level { get; set; }
  85 +
  86 + // 是否为年费黄钻用户
  87 + public int is_yellow_year_vip { get; set; }
  88 + }
  89 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Yi.Framework.AspNetCore.Authentication.OAuth.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/Yi.Framework.AspNetCore.Authentication.OAuth.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 + <Import Project="..\..\common.props" />
  3 + <PropertyGroup>
  4 + <TargetFramework>net8.0</TargetFramework>
  5 + <ImplicitUsings>enable</ImplicitUsings>
  6 + <Nullable>enable</Nullable>
  7 + </PropertyGroup>
  8 + <ItemGroup>
  9 + <ProjectReference Include="..\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj" />
  10 + </ItemGroup>
  11 +
  12 +</Project>
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/YiFrameworkAspNetCoreAuthenticationOAuthModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore.Authentication.OAuth/YiFrameworkAspNetCoreAuthenticationOAuthModule.cs
  1 +using Microsoft.Extensions.DependencyInjection;
  2 +using Volo.Abp.Modularity;
  3 +using Yi.Framework.Core;
  4 +
  5 +
  6 +namespace Yi.Framework.AspNetCore.Authentication.OAuth
  7 +{
  8 + /// <summary>
  9 + /// 本模块轮子来自 AspNet.Security.OAuth.QQ;
  10 + /// </summary>
  11 + [DependsOn(typeof(YiFrameworkAspNetCoreModule))]
  12 + public class YiFrameworkAspNetCoreAuthenticationOAuthModule:AbpModule
  13 + {
  14 + public override void ConfigureServices(ServiceConfigurationContext context)
  15 + {
  16 + var service = context.Services;
  17 + service.AddHttpClient();
  18 + }
  19 + }
  20 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Builder/ApiInfoBuilderExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Builder/ApiInfoBuilderExtensions.cs
  1 +using JetBrains.Annotations;
  2 +using Microsoft.AspNetCore.Builder;
  3 +using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Middlewares;
  4 +
  5 +namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder
  6 +{
  7 + /// <summary>
  8 + /// 提供API信息处理的应用程序构建器扩展方法
  9 + /// </summary>
  10 + public static class ApiInfoBuilderExtensions
  11 + {
  12 + /// <summary>
  13 + /// 使用Yi框架的API信息处理中间件
  14 + /// </summary>
  15 + /// <param name="builder">应用程序构建器实例</param>
  16 + /// <returns>配置后的应用程序构建器实例</returns>
  17 + /// <exception cref="ArgumentNullException">当builder参数为null时抛出</exception>
  18 + public static IApplicationBuilder UseApiInfoHandling([NotNull] this IApplicationBuilder builder)
  19 + {
  20 + // 添加API信息处理中间件到请求管道
  21 + builder.UseMiddleware<ApiInfoMiddleware>();
  22 +
  23 + return builder;
  24 + }
  25 + }
  26 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Builder/SwaggerBuilderExtensons.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Builder/SwaggerBuilderExtensons.cs
  1 +using Microsoft.AspNetCore.Builder;
  2 +using Microsoft.Extensions.DependencyInjection;
  3 +using Microsoft.Extensions.Options;
  4 +using Volo.Abp.AspNetCore.Mvc;
  5 +
  6 +namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder
  7 +{
  8 + /// <summary>
  9 + /// Swagger构建器扩展类
  10 + /// </summary>
  11 + public static class SwaggerBuilderExtensions
  12 + {
  13 + /// <summary>
  14 + /// 配置并使用Yi框架的Swagger中间件
  15 + /// </summary>
  16 + /// <param name="app">应用程序构建器</param>
  17 + /// <param name="swaggerConfigs">Swagger配置模型数组</param>
  18 + /// <returns>应用程序构建器</returns>
  19 + public static IApplicationBuilder UseYiSwagger(
  20 + this IApplicationBuilder app,
  21 + params SwaggerConfiguration[] swaggerConfigs)
  22 + {
  23 + if (app == null)
  24 + {
  25 + throw new ArgumentNullException(nameof(app));
  26 + }
  27 +
  28 + var mvcOptions = app.ApplicationServices
  29 + .GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>()
  30 + .Value;
  31 +
  32 + // 启用Swagger中间件
  33 + app.UseSwagger();
  34 +
  35 + // 配置SwaggerUI
  36 + app.UseSwaggerUI(options =>
  37 + {
  38 + // 添加约定控制器的Swagger终结点
  39 + var conventionalSettings = mvcOptions.ConventionalControllers.ConventionalControllerSettings;
  40 + foreach (var setting in conventionalSettings)
  41 + {
  42 + options.SwaggerEndpoint(
  43 + $"/swagger/{setting.RemoteServiceName}/swagger.json",
  44 + setting.RemoteServiceName);
  45 + }
  46 +
  47 + // 如果没有配置任何终结点,使用默认配置
  48 + if (!conventionalSettings.Any() && (swaggerConfigs == null || !swaggerConfigs.Any()))
  49 + {
  50 + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Yi.Framework");
  51 + return;
  52 + }
  53 +
  54 + // 添加自定义Swagger配置的终结点
  55 + if (swaggerConfigs != null)
  56 + {
  57 + foreach (var config in swaggerConfigs)
  58 + {
  59 + options.SwaggerEndpoint(config.Url, config.Name);
  60 + }
  61 + }
  62 + });
  63 +
  64 + return app;
  65 + }
  66 + }
  67 +
  68 + /// <summary>
  69 + /// Swagger配置模型
  70 + /// </summary>
  71 + public class SwaggerConfiguration
  72 + {
  73 + private const string DefaultSwaggerUrl = "/swagger/v1/swagger.json";
  74 +
  75 + /// <summary>
  76 + /// Swagger JSON文档的URL
  77 + /// </summary>
  78 + public string Url { get; }
  79 +
  80 + /// <summary>
  81 + /// Swagger文档的显示名称
  82 + /// </summary>
  83 + public string Name { get; }
  84 +
  85 + /// <summary>
  86 + /// 使用默认URL创建Swagger配置
  87 + /// </summary>
  88 + /// <param name="name">文档显示名称</param>
  89 + public SwaggerConfiguration(string name)
  90 + : this(DefaultSwaggerUrl, name)
  91 + {
  92 + }
  93 +
  94 + /// <summary>
  95 + /// 创建自定义Swagger配置
  96 + /// </summary>
  97 + /// <param name="url">Swagger JSON文档URL</param>
  98 + /// <param name="name">文档显示名称</param>
  99 + public SwaggerConfiguration(string url, string name)
  100 + {
  101 + Url = url ?? throw new ArgumentNullException(nameof(url));
  102 + Name = name ?? throw new ArgumentNullException(nameof(name));
  103 + }
  104 + }
  105 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Middlewares/ApiInfoMiddleware.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/AspNetCore/Middlewares/ApiInfoMiddleware.cs
  1 +using System.Diagnostics;
  2 +using Microsoft.AspNetCore.Http;
  3 +using Volo.Abp.DependencyInjection;
  4 +using Yi.Framework.Core.Extensions;
  5 +
  6 +namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Middlewares
  7 +{
  8 + /// <summary>
  9 + /// API响应信息处理中间件
  10 + /// 主要用于处理特定文件类型的响应头信息
  11 + /// </summary>
  12 + [DebuggerStepThrough]
  13 + public class ApiInfoMiddleware : IMiddleware, ITransientDependency
  14 + {
  15 + /// <summary>
  16 + /// 处理HTTP请求的中间件方法
  17 + /// </summary>
  18 + /// <param name="context">HTTP上下文</param>
  19 + /// <param name="next">请求处理委托</param>
  20 + /// <returns>异步任务</returns>
  21 + public async Task InvokeAsync(HttpContext context, RequestDelegate next)
  22 + {
  23 + // // 在响应开始时处理文件下载相关的响应头
  24 + // context.Response.OnStarting(() =>
  25 + // {
  26 + // HandleFileDownloadResponse(context);
  27 + // return Task.CompletedTask;
  28 + // });
  29 +
  30 + // 继续处理管道中的下一个中间件
  31 + await next(context);
  32 + }
  33 +
  34 + /// <summary>
  35 + /// 处理文件下载响应的响应头信息
  36 + /// </summary>
  37 + /// <param name="context">HTTP上下文</param>
  38 + private static void HandleFileDownloadResponse(HttpContext context)
  39 + {
  40 + // 仅处理状态码为200的响应
  41 + if (context.Response.StatusCode != StatusCodes.Status200OK)
  42 + {
  43 + return;
  44 + }
  45 +
  46 + var contentType = context.Response.Headers["Content-Type"].ToString();
  47 + var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
  48 +
  49 + // 处理Excel文件下载
  50 + if (contentType == "application/vnd.ms-excel")
  51 + {
  52 + context.FileAttachmentHandle($"{timestamp}.xlsx");
  53 + }
  54 + // 处理ZIP文件下载
  55 + else if (contentType == "application/x-zip-compressed")
  56 + {
  57 + context.FileAttachmentHandle($"{timestamp}.zip");
  58 + }
  59 + }
  60 + }
  61 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs
  1 +using System.ComponentModel;
  2 +using System.Diagnostics;
  3 +using System.Text;
  4 +using System.Xml.Linq;
  5 +using Microsoft.AspNetCore.Mvc.Controllers;
  6 +using Microsoft.Extensions.DependencyInjection;
  7 +using Microsoft.Extensions.Options;
  8 +using Microsoft.OpenApi.Any;
  9 +using Microsoft.OpenApi.Models;
  10 +using Swashbuckle.AspNetCore.SwaggerGen;
  11 +using Volo.Abp.AspNetCore.Mvc;
  12 +using Volo.Abp.AspNetCore.Mvc.Conventions;
  13 +using Volo.Abp.DependencyInjection;
  14 +using Volo.Abp.Options;
  15 +
  16 +namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
  17 +{
  18 + /// <summary>
  19 + /// Swagger生成器扩展类
  20 + /// </summary>
  21 + public static class SwaggerAddExtensions
  22 + {
  23 + /// <summary>
  24 + /// 添加Yi框架的Swagger生成器服务
  25 + /// </summary>
  26 + /// <typeparam name="TProgram">程序入口类型</typeparam>
  27 + /// <param name="services">服务集合</param>
  28 + /// <param name="setupAction">自定义配置动作</param>
  29 + /// <returns>服务集合</returns>
  30 + public static IServiceCollection AddYiSwaggerGen<TProgram>(
  31 + this IServiceCollection services,
  32 + Action<SwaggerGenOptions>? setupAction = null)
  33 + {
  34 + // 获取MVC配置选项
  35 + var mvcOptions = services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>().Configure();
  36 +
  37 + // 获取并去重远程服务名称
  38 + var remoteServiceSettings = mvcOptions.ConventionalControllers
  39 + .ConventionalControllerSettings
  40 + .DistinctBy(x => x.RemoteServiceName);
  41 +
  42 + services.AddAbpSwaggerGen(
  43 + options =>
  44 + {
  45 + // 应用外部配置
  46 + setupAction?.Invoke(options);
  47 +
  48 + // 配置API文档分组
  49 + ConfigureApiGroups(options, remoteServiceSettings);
  50 +
  51 + // 配置API文档过滤器
  52 + ConfigureApiFilter(options, remoteServiceSettings);
  53 +
  54 + // 配置Schema ID生成规则
  55 + options.CustomSchemaIds(type => type.FullName);
  56 +
  57 + // 包含XML注释文档
  58 + IncludeXmlComments<TProgram>(options);
  59 +
  60 + // 配置JWT认证
  61 + ConfigureJwtAuthentication(options);
  62 +
  63 + // 添加自定义过滤器
  64 + ConfigureCustomFilters(options);
  65 + }
  66 + );
  67 +
  68 + return services;
  69 + }
  70 +
  71 + /// <summary>
  72 + /// 配置API分组
  73 + /// </summary>
  74 + private static void ConfigureApiGroups(
  75 + SwaggerGenOptions options,
  76 + IEnumerable<ConventionalControllerSetting> settings)
  77 + {
  78 + foreach (var setting in settings.OrderBy(x => x.RemoteServiceName))
  79 + {
  80 + if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
  81 + {
  82 + options.SwaggerDoc(setting.RemoteServiceName, new OpenApiInfo
  83 + {
  84 + Title = setting.RemoteServiceName,
  85 + Version = "v1"
  86 + });
  87 + }
  88 + }
  89 + }
  90 +
  91 + /// <summary>
  92 + /// 配置API文档过滤器
  93 + /// </summary>
  94 + private static void ConfigureApiFilter(
  95 + SwaggerGenOptions options,
  96 + IEnumerable<ConventionalControllerSetting> settings)
  97 + {
  98 + options.DocInclusionPredicate((docName, apiDesc) =>
  99 + {
  100 + if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerDesc)
  101 + {
  102 + var matchedSetting = settings
  103 + .FirstOrDefault(x => x.Assembly == controllerDesc.ControllerTypeInfo.Assembly);
  104 + return matchedSetting?.RemoteServiceName == docName;
  105 + }
  106 + return false;
  107 + });
  108 + }
  109 +
  110 + /// <summary>
  111 + /// 包含XML注释文档
  112 + /// </summary>
  113 + private static void IncludeXmlComments<TProgram>(SwaggerGenOptions options)
  114 + {
  115 + var basePath = Path.GetDirectoryName(typeof(TProgram).Assembly.Location);
  116 + if (basePath is not null)
  117 + {
  118 + foreach (var xmlFile in Directory.GetFiles(basePath, "*.xml"))
  119 + {
  120 + options.IncludeXmlComments(xmlFile, true);
  121 + }
  122 + }
  123 + }
  124 +
  125 + /// <summary>
  126 + /// 配置JWT认证
  127 + /// </summary>
  128 + private static void ConfigureJwtAuthentication(SwaggerGenOptions options)
  129 + {
  130 + options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme
  131 + {
  132 + Description = "请在此输入JWT Token",
  133 + Name = "Authorization",
  134 + In = ParameterLocation.Header,
  135 + Type = SecuritySchemeType.Http,
  136 + Scheme = "bearer"
  137 + });
  138 +
  139 + var scheme = new OpenApiSecurityScheme
  140 + {
  141 + Reference = new OpenApiReference
  142 + {
  143 + Type = ReferenceType.SecurityScheme,
  144 + Id = "JwtBearer"
  145 + }
  146 + };
  147 +
  148 + options.AddSecurityRequirement(new OpenApiSecurityRequirement
  149 + {
  150 + [scheme] = Array.Empty<string>()
  151 + });
  152 + }
  153 +
  154 + /// <summary>
  155 + /// 配置自定义过滤器
  156 + /// </summary>
  157 + private static void ConfigureCustomFilters(SwaggerGenOptions options)
  158 + {
  159 + options.OperationFilter<TenantHeaderOperationFilter>();
  160 + options.SchemaFilter<EnumSchemaFilter>();
  161 + }
  162 + }
  163 +
  164 + /// <summary>
  165 + /// Swagger文档枚举字段显示过滤器
  166 + /// </summary>
  167 + public class EnumSchemaFilter : ISchemaFilter
  168 + {
  169 + /// <summary>
  170 + /// 应用枚举架构过滤器
  171 + /// </summary>
  172 + /// <param name="schema">OpenAPI架构</param>
  173 + /// <param name="context">架构过滤器上下文</param>
  174 + public void Apply(OpenApiSchema schema, SchemaFilterContext context)
  175 + {
  176 + if (!context.Type.IsEnum) return;
  177 +
  178 + schema.Enum.Clear();
  179 + schema.Type = "string";
  180 + schema.Format = null;
  181 +
  182 + var enumDescriptions = new StringBuilder();
  183 + foreach (var enumName in Enum.GetNames(context.Type))
  184 + {
  185 + var enumValue = (Enum)Enum.Parse(context.Type, enumName);
  186 + var description = GetEnumDescription(enumValue);
  187 + var enumIntValue = Convert.ToInt64(enumValue);
  188 +
  189 + schema.Enum.Add(new OpenApiString(enumName));
  190 + enumDescriptions.AppendLine(
  191 + $"【枚举:{enumName}{(description is null ? string.Empty : $"({description})")}={enumIntValue}】");
  192 + }
  193 + schema.Description = enumDescriptions.ToString();
  194 + }
  195 +
  196 + /// <summary>
  197 + /// 获取枚举描述特性值
  198 + /// </summary>
  199 + private static string? GetEnumDescription(Enum value)
  200 + {
  201 + var fieldInfo = value.GetType().GetField(value.ToString());
  202 + var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
  203 + return attributes.Length > 0 ? attributes[0].Description : null;
  204 + }
  205 + }
  206 +
  207 + /// <summary>
  208 + /// 租户头部参数过滤器
  209 + /// </summary>
  210 + public class TenantHeaderOperationFilter : IOperationFilter
  211 + {
  212 + /// <summary>
  213 + /// 租户标识键名
  214 + /// </summary>
  215 + private const string TenantHeaderKey = "__tenant";
  216 +
  217 + /// <summary>
  218 + /// 应用租户头部参数过滤器
  219 + /// </summary>
  220 + public void Apply(OpenApiOperation operation, OperationFilterContext context)
  221 + {
  222 + operation.Parameters ??= new List<OpenApiParameter>();
  223 +
  224 + operation.Parameters.Add(new OpenApiParameter
  225 + {
  226 + Name = TenantHeaderKey,
  227 + In = ParameterLocation.Header,
  228 + Required = false,
  229 + AllowEmptyValue = true,
  230 + Description = "租户ID或租户名称(留空表示默认租户)"
  231 + });
  232 + }
  233 + }
  234 +}
0 235 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Mvc/YiConventionalRouteBuilder.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Mvc/YiConventionalRouteBuilder.cs
  1 +using JetBrains.Annotations;
  2 +using Microsoft.AspNetCore.Mvc.ApplicationModels;
  3 +using System.Reflection;
  4 +using Microsoft.Extensions.DependencyInjection;
  5 +using Microsoft.Extensions.Options;
  6 +using Volo.Abp.AspNetCore.Mvc.Conventions;
  7 +using Volo.Abp.DependencyInjection;
  8 +using Volo.Abp.Reflection;
  9 +
  10 +namespace Yi.Framework.AspNetCore.Mvc
  11 +{
  12 + /// <summary>
  13 + /// 自定义路由构建器,用于生成API路由规则
  14 + /// </summary>
  15 + [Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
  16 + [ExposeServices(typeof(IConventionalRouteBuilder))]
  17 + public class YiConventionalRouteBuilder : ConventionalRouteBuilder
  18 + {
  19 + /// <summary>
  20 + /// 构造函数
  21 + /// </summary>
  22 + /// <param name="options">ABP约定控制器配置选项</param>
  23 + public YiConventionalRouteBuilder(IOptions<AbpConventionalControllerOptions> options)
  24 + : base(options)
  25 + {
  26 + }
  27 +
  28 + /// <summary>
  29 + /// 构建API路由
  30 + /// </summary>
  31 + /// <param name="rootPath">根路径</param>
  32 + /// <param name="controllerName">控制器名称</param>
  33 + /// <param name="action">Action模型</param>
  34 + /// <param name="httpMethod">HTTP方法</param>
  35 + /// <param name="configuration">控制器配置</param>
  36 + /// <returns>构建的路由URL</returns>
  37 + public override string Build(
  38 + string rootPath,
  39 + string controllerName,
  40 + ActionModel action,
  41 + string httpMethod,
  42 + [CanBeNull] ConventionalControllerSetting configuration)
  43 + {
  44 + // 获取API路由前缀
  45 + var apiRoutePrefix = GetApiRoutePrefix(action, configuration);
  46 +
  47 + // 规范化控制器名称
  48 + var normalizedControllerName = NormalizeUrlControllerName(
  49 + rootPath,
  50 + controllerName,
  51 + action,
  52 + httpMethod,
  53 + configuration);
  54 +
  55 + // 构建基础URL
  56 + var url = $"{rootPath}/{NormalizeControllerNameCase(normalizedControllerName, configuration)}";
  57 +
  58 + // 处理ID参数路由
  59 + url = BuildIdParameterRoute(url, action, configuration);
  60 +
  61 + // 处理Action名称路由
  62 + url = BuildActionNameRoute(url, rootPath, controllerName, action, httpMethod, configuration);
  63 +
  64 + return url;
  65 + }
  66 +
  67 + /// <summary>
  68 + /// 构建ID参数路由部分
  69 + /// </summary>
  70 + private string BuildIdParameterRoute(
  71 + string baseUrl,
  72 + ActionModel action,
  73 + ConventionalControllerSetting configuration)
  74 + {
  75 + var idParameter = action.Parameters.FirstOrDefault(p => p.ParameterName == "id");
  76 + if (idParameter == null)
  77 + {
  78 + return baseUrl;
  79 + }
  80 +
  81 + // 处理原始类型ID
  82 + if (TypeHelper.IsPrimitiveExtended(idParameter.ParameterType, includeEnums: true))
  83 + {
  84 + return $"{baseUrl}/{{id}}";
  85 + }
  86 +
  87 + // 处理复杂类型ID
  88 + var properties = idParameter.ParameterType
  89 + .GetProperties(BindingFlags.Instance | BindingFlags.Public);
  90 +
  91 + foreach (var property in properties)
  92 + {
  93 + baseUrl += $"/{{{NormalizeIdPropertyNameCase(property, configuration)}}}";
  94 + }
  95 +
  96 + return baseUrl;
  97 + }
  98 +
  99 + /// <summary>
  100 + /// 构建Action名称路由部分
  101 + /// </summary>
  102 + private string BuildActionNameRoute(
  103 + string baseUrl,
  104 + string rootPath,
  105 + string controllerName,
  106 + ActionModel action,
  107 + string httpMethod,
  108 + ConventionalControllerSetting configuration)
  109 + {
  110 + var actionNameInUrl = NormalizeUrlActionName(
  111 + rootPath,
  112 + controllerName,
  113 + action,
  114 + httpMethod,
  115 + configuration);
  116 +
  117 + if (actionNameInUrl.IsNullOrEmpty())
  118 + {
  119 + return baseUrl;
  120 + }
  121 +
  122 + baseUrl += $"/{NormalizeActionNameCase(actionNameInUrl, configuration)}";
  123 +
  124 + // 处理次要ID参数
  125 + var secondaryIds = action.Parameters
  126 + .Where(p => p.ParameterName.EndsWith("Id", StringComparison.Ordinal))
  127 + .ToList();
  128 +
  129 + if (secondaryIds.Count == 1)
  130 + {
  131 + baseUrl += $"/{{{NormalizeSecondaryIdNameCase(secondaryIds[0], configuration)}}}";
  132 + }
  133 +
  134 + return baseUrl;
  135 + }
  136 + }
  137 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Mvc/YiServiceConvention.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Mvc/YiServiceConvention.cs
  1 +using JetBrains.Annotations;
  2 +using Microsoft.AspNetCore.Mvc;
  3 +using Microsoft.AspNetCore.Mvc.ActionConstraints;
  4 +using Microsoft.AspNetCore.Mvc.ApplicationModels;
  5 +using Microsoft.Extensions.DependencyInjection;
  6 +using Microsoft.Extensions.Options;
  7 +using Volo.Abp;
  8 +using Volo.Abp.AspNetCore;
  9 +using Volo.Abp.AspNetCore.Mvc;
  10 +using Volo.Abp.AspNetCore.Mvc.Conventions;
  11 +using Volo.Abp.DependencyInjection;
  12 +using Volo.Abp.Reflection;
  13 +
  14 +namespace Yi.Framework.AspNetCore.Mvc
  15 +{
  16 + /// <summary>
  17 + /// 自定义服务约定实现,用于处理API路由和HTTP方法约束
  18 + /// </summary>
  19 + [Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
  20 + [ExposeServices(typeof(IAbpServiceConvention))]
  21 + public class YiServiceConvention : AbpServiceConvention
  22 + {
  23 + /// <summary>
  24 + /// 初始化服务约定的新实例
  25 + /// </summary>
  26 + /// <param name="options">ABP AspNetCore MVC 配置选项</param>
  27 + /// <param name="conventionalRouteBuilder">约定路由构建器</param>
  28 + public YiServiceConvention(
  29 + IOptions<AbpAspNetCoreMvcOptions> options,
  30 + IConventionalRouteBuilder conventionalRouteBuilder)
  31 + : base(options, conventionalRouteBuilder)
  32 + {
  33 + }
  34 +
  35 + /// <summary>
  36 + /// 配置选择器,处理路由和HTTP方法约束
  37 + /// </summary>
  38 + protected override void ConfigureSelector(
  39 + string rootPath,
  40 + string controllerName,
  41 + ActionModel action,
  42 + ConventionalControllerSetting? configuration)
  43 + {
  44 + // 移除空选择器
  45 + RemoveEmptySelectors(action.Selectors);
  46 +
  47 + // 检查远程服务特性
  48 + var remoteServiceAttr = ReflectionHelper
  49 + .GetSingleAttributeOrDefault<RemoteServiceAttribute>(action.ActionMethod);
  50 + if (remoteServiceAttr != null && !remoteServiceAttr.IsEnabledFor(action.ActionMethod))
  51 + {
  52 + return;
  53 + }
  54 +
  55 + // 根据选择器是否存在执行不同的配置
  56 + if (!action.Selectors.Any())
  57 + {
  58 + AddAbpServiceSelector(rootPath, controllerName, action, configuration);
  59 + }
  60 + else
  61 + {
  62 + NormalizeSelectorRoutes(rootPath, controllerName, action, configuration);
  63 + }
  64 + }
  65 +
  66 + /// <summary>
  67 + /// 规范化选择器路由
  68 + /// </summary>
  69 + protected override void NormalizeSelectorRoutes(
  70 + string rootPath,
  71 + string controllerName,
  72 + ActionModel action,
  73 + ConventionalControllerSetting? configuration)
  74 + {
  75 + foreach (var selector in action.Selectors)
  76 + {
  77 + // 获取HTTP方法约束
  78 + var httpMethod = GetOrCreateHttpMethod(selector, action, configuration);
  79 +
  80 + // 处理路由模板
  81 + ConfigureRouteTemplate(selector, rootPath, controllerName, action, httpMethod, configuration);
  82 +
  83 + // 确保HTTP方法约束存在
  84 + EnsureHttpMethodConstraint(selector, httpMethod);
  85 + }
  86 + }
  87 +
  88 + /// <summary>
  89 + /// 获取或创建HTTP方法
  90 + /// </summary>
  91 + private string GetOrCreateHttpMethod(
  92 + SelectorModel selector,
  93 + ActionModel action,
  94 + ConventionalControllerSetting? configuration)
  95 + {
  96 + return selector.ActionConstraints
  97 + .OfType<HttpMethodActionConstraint>()
  98 + .FirstOrDefault()?
  99 + .HttpMethods?
  100 + .FirstOrDefault()
  101 + ?? SelectHttpMethod(action, configuration);
  102 + }
  103 +
  104 + /// <summary>
  105 + /// 配置路由模板
  106 + /// </summary>
  107 + private void ConfigureRouteTemplate(
  108 + SelectorModel selector,
  109 + string rootPath,
  110 + string controllerName,
  111 + ActionModel action,
  112 + string httpMethod,
  113 + ConventionalControllerSetting? configuration)
  114 + {
  115 + if (selector.AttributeRouteModel == null)
  116 + {
  117 + selector.AttributeRouteModel = CreateAbpServiceAttributeRouteModel(
  118 + rootPath,
  119 + controllerName,
  120 + action,
  121 + httpMethod,
  122 + configuration);
  123 + }
  124 + else
  125 + {
  126 + NormalizeAttributeRouteTemplate(selector, rootPath);
  127 + }
  128 + }
  129 +
  130 + /// <summary>
  131 + /// 规范化特性路由模板
  132 + /// </summary>
  133 + private void NormalizeAttributeRouteTemplate(SelectorModel selector, string rootPath)
  134 + {
  135 + var template = selector.AttributeRouteModel.Template;
  136 + if (!template.StartsWith("/"))
  137 + {
  138 + selector.AttributeRouteModel.Template = $"{rootPath}/{template}";
  139 + }
  140 + }
  141 +
  142 + /// <summary>
  143 + /// 确保HTTP方法约束存在
  144 + /// </summary>
  145 + private void EnsureHttpMethodConstraint(SelectorModel selector, string httpMethod)
  146 + {
  147 + if (!selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Any())
  148 + {
  149 + selector.ActionConstraints.Add(
  150 + new HttpMethodActionConstraint(new[] { httpMethod }));
  151 + }
  152 + }
  153 + }
  154 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/RealIpHttpContextWebClientInfoProvider.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/RealIpHttpContextWebClientInfoProvider.cs
  1 +using System.Net;
  2 +using Microsoft.AspNetCore.Http;
  3 +using Microsoft.Extensions.Logging;
  4 +using Volo.Abp.AspNetCore.WebClientInfo;
  5 +
  6 +namespace Yi.Framework.AspNetCore;
  7 +
  8 +/// <summary>
  9 +/// 真实IP地址提供程序,支持代理服务器场景
  10 +/// </summary>
  11 +public class RealIpHttpContextWebClientInfoProvider : HttpContextWebClientInfoProvider
  12 +{
  13 + private const string XForwardedForHeader = "X-Forwarded-For";
  14 +
  15 + /// <summary>
  16 + /// 初始化真实IP地址提供程序的新实例
  17 + /// </summary>
  18 + public RealIpHttpContextWebClientInfoProvider(
  19 + ILogger<HttpContextWebClientInfoProvider> logger,
  20 + IHttpContextAccessor httpContextAccessor)
  21 + : base(logger, httpContextAccessor)
  22 + {
  23 + }
  24 +
  25 + /// <summary>
  26 + /// 获取客户端IP地址,优先从X-Forwarded-For头部获取
  27 + /// </summary>
  28 + /// <returns>客户端IP地址</returns>
  29 + protected override string? GetClientIpAddress()
  30 + {
  31 + try
  32 + {
  33 + var httpContext = HttpContextAccessor.HttpContext;
  34 + if (httpContext == null)
  35 + {
  36 + return null;
  37 + }
  38 +
  39 + var headers = httpContext.Request?.Headers;
  40 + if (headers != null && headers.ContainsKey(XForwardedForHeader))
  41 + {
  42 + // 从X-Forwarded-For获取真实客户端IP
  43 + var forwardedIp = headers[XForwardedForHeader].FirstOrDefault();
  44 + if (!string.IsNullOrEmpty(forwardedIp))
  45 + {
  46 + httpContext.Connection.RemoteIpAddress = IPAddress.Parse(forwardedIp);
  47 + }
  48 + }
  49 +
  50 + return httpContext.Connection?.RemoteIpAddress?.ToString();
  51 + }
  52 + catch (Exception ex)
  53 + {
  54 + Logger.LogWarning(ex, "获取客户端IP地址时发生异常");
  55 + return null;
  56 + }
  57 + }
  58 +}
0 59 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/RemoteServiceSuccessInfo.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/RemoteServiceSuccessInfo.cs
  1 +namespace Yi.Framework.AspNetCore
  2 +{
  3 + /// <summary>
  4 + /// 远程服务成功响应信息
  5 + /// </summary>
  6 + [Serializable]
  7 + public class RemoteServiceSuccessInfo
  8 + {
  9 + /// <summary>
  10 + /// 获取或设置响应代码
  11 + /// </summary>
  12 + public string? Code { get; private set; }
  13 +
  14 + /// <summary>
  15 + /// 获取或设置响应消息
  16 + /// </summary>
  17 + public string? Message { get; private set; }
  18 +
  19 + /// <summary>
  20 + /// 获取或设置详细信息
  21 + /// </summary>
  22 + public string? Details { get; private set; }
  23 +
  24 + /// <summary>
  25 + /// 获取或设置响应数据
  26 + /// </summary>
  27 + public object? Data { get; private set; }
  28 +
  29 + /// <summary>
  30 + /// 初始化远程服务成功响应信息的新实例
  31 + /// </summary>
  32 + public RemoteServiceSuccessInfo()
  33 + {
  34 + }
  35 +
  36 + /// <summary>
  37 + /// 使用指定参数初始化远程服务成功响应信息的新实例
  38 + /// </summary>
  39 + /// <param name="message">响应消息</param>
  40 + /// <param name="details">详细信息</param>
  41 + /// <param name="code">响应代码</param>
  42 + /// <param name="data">响应数据</param>
  43 + public RemoteServiceSuccessInfo(
  44 + string message,
  45 + string? details = null,
  46 + string? code = null,
  47 + object? data = null)
  48 + {
  49 + Message = message;
  50 + Details = details;
  51 + Code = code;
  52 + Data = data;
  53 + }
  54 + }
  55 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ExceptionMetadata.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ExceptionMetadata.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +namespace Yi.Framework.AspNetCore.UnifyResult;
  16 +
  17 +/// <summary>
  18 +/// 异常元数据
  19 +/// </summary>
  20 +public sealed class ExceptionMetadata
  21 +{
  22 + /// <summary>
  23 + /// 状态码
  24 + /// </summary>
  25 + public int StatusCode { get; internal set; }
  26 +
  27 + /// <summary>
  28 + /// 错误码
  29 + /// </summary>
  30 + public object ErrorCode { get; internal set; }
  31 +
  32 + /// <summary>
  33 + /// 错误码(没被复写过的 ErrorCode )
  34 + /// </summary>
  35 + public object OriginErrorCode { get; internal set; }
  36 +
  37 + /// <summary>
  38 + /// 错误对象(信息)
  39 + /// </summary>
  40 + public object Errors { get; internal set; }
  41 +
  42 + /// <summary>
  43 + /// 额外数据
  44 + /// </summary>
  45 + public object Data { get; internal set; }
  46 +}
0 47 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/FriendlyExceptionFilter.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/FriendlyExceptionFilter.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +using Microsoft.AspNetCore.Http;
  16 +using Microsoft.AspNetCore.Mvc;
  17 +using Microsoft.AspNetCore.Mvc.Filters;
  18 +using Microsoft.Extensions.DependencyInjection;
  19 +using Microsoft.Extensions.Logging;
  20 +using Newtonsoft.Json;
  21 +using Volo.Abp.AspNetCore.Mvc;
  22 +using Volo.Abp.Validation;
  23 +using Yi.Framework.Core.Extensions;
  24 +
  25 +namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
  26 +
  27 +/// <summary>
  28 +/// 友好异常拦截器
  29 +/// </summary>
  30 +public sealed class FriendlyExceptionFilter : IAsyncExceptionFilter
  31 +{
  32 + /// <summary>
  33 + /// 异常拦截
  34 + /// </summary>
  35 + /// <param name="context"></param>
  36 + /// <returns></returns>
  37 + public async Task OnExceptionAsync(ExceptionContext context)
  38 + {
  39 + // 排除 WebSocket 请求处理
  40 + if (context.HttpContext.IsWebSocketRequest()) return;
  41 +
  42 + // 如果异常在其他地方被标记了处理,那么这里不再处理
  43 + if (context.ExceptionHandled) return;
  44 +
  45 + // 解析异常信息
  46 + var exceptionMetadata = GetExceptionMetadata(context);
  47 + var unifyResult = context.GetRequiredService<IUnifyResultProvider>();
  48 + // 执行规范化异常处理
  49 + context.Result = unifyResult.OnException(context, exceptionMetadata);
  50 +
  51 + // 创建日志记录器
  52 + var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<FriendlyExceptionFilter>>();
  53 +
  54 + var errorMsg = "";
  55 + if (exceptionMetadata.Errors != null) errorMsg = "\n" + JsonConvert.SerializeObject(exceptionMetadata.Errors);
  56 +
  57 +
  58 + // 记录拦截日常
  59 + logger.LogError(context.Exception, context.Exception.Message + errorMsg);
  60 + }
  61 +
  62 + /// <summary>
  63 + /// 获取异常元数据
  64 + /// </summary>
  65 + /// <param name="context"></param>
  66 + /// <returns></returns>
  67 + public static ExceptionMetadata GetExceptionMetadata(ActionContext context)
  68 + {
  69 + object errorCode = default;
  70 + object originErrorCode = default;
  71 + object errors = default;
  72 + object data = default;
  73 + var statusCode = StatusCodes.Status500InternalServerError;
  74 + var isValidationException = false; // 判断是否是验证异常
  75 + var isFriendlyException = false;
  76 +
  77 + // 判断是否是 ExceptionContext 或者 ActionExecutedContext
  78 + var exception = context is ExceptionContext exContext
  79 + ? exContext.Exception
  80 + : context is ActionExecutedContext edContext
  81 + ? edContext.Exception
  82 + : default;
  83 +
  84 + if (exception is AbpValidationException validationException)
  85 + {
  86 + errors = validationException.ValidationErrors;
  87 + isValidationException = true;
  88 + }
  89 +
  90 + // 判断是否是友好异常
  91 + if (exception is UserFriendlyException friendlyException)
  92 + {
  93 + var statusCode2 = 500;
  94 + int.TryParse(friendlyException.Code, out statusCode2);
  95 + isFriendlyException = true;
  96 + errorCode = friendlyException.Code;
  97 + originErrorCode = friendlyException.Code;
  98 + statusCode = statusCode2 == 0 ? 403 : statusCode2;
  99 + errors = friendlyException.Message;
  100 + data = friendlyException.Data;
  101 + }
  102 +
  103 + return new ExceptionMetadata
  104 + {
  105 + StatusCode = statusCode,
  106 + ErrorCode = errorCode,
  107 + OriginErrorCode = originErrorCode,
  108 + Errors = errors,
  109 + Data = data
  110 + };
  111 + }
  112 +}
0 113 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/SucceededUnifyResultFilter.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/SucceededUnifyResultFilter.cs
  1 +using System.Collections;
  2 +using System.Reflection;
  3 +using System.Text.Encodings.Web;
  4 +using System.Text.Json;
  5 +using Microsoft.AspNetCore.Http;
  6 +using Microsoft.AspNetCore.Http.Features;
  7 +using Microsoft.AspNetCore.Mvc;
  8 +using Microsoft.AspNetCore.Mvc.ApiExplorer;
  9 +using Microsoft.AspNetCore.Mvc.Controllers;
  10 +using Microsoft.AspNetCore.Mvc.Filters;
  11 +using Microsoft.AspNetCore.Mvc.Infrastructure;
  12 +using Microsoft.AspNetCore.Mvc.ModelBinding;
  13 +using Microsoft.AspNetCore.Mvc.RazorPages;
  14 +using Microsoft.Extensions.DependencyInjection;
  15 +using Microsoft.Extensions.Options;
  16 +using Volo.Abp.AspNetCore.Mvc;
  17 +using Volo.Abp.DependencyInjection;
  18 +using Yi.Framework.Core.Extensions;
  19 +
  20 +namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
  21 +
  22 +/// <summary>
  23 +/// 规范化结构(请求成功)过滤器
  24 +/// </summary>
  25 +public class SucceededUnifyResultFilter : IAsyncActionFilter, IOrderedFilter
  26 +{
  27 + /// <summary>
  28 + /// 过滤器排序
  29 + /// </summary>
  30 + private const int FilterOrder = 8888;
  31 +
  32 + /// <summary>
  33 + /// 排序属性
  34 + /// </summary>
  35 + public int Order => FilterOrder;
  36 +
  37 + /// <summary>
  38 + /// 处理规范化结果
  39 + /// </summary>
  40 + /// <param name="context"></param>
  41 + /// <param name="next"></param>
  42 + /// <returns></returns>
  43 + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  44 + {
  45 + // 执行 Action 并获取结果
  46 + var actionExecutedContext = await next();
  47 +
  48 + // 排除 WebSocket 请求处理
  49 + if (actionExecutedContext.HttpContext.IsWebSocketRequest()) return;
  50 +
  51 + // 处理已经含有状态码结果的 Result
  52 + if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult &&
  53 + statusCodeResult.StatusCode != null)
  54 + {
  55 + // 小于 200 或者 大于 299 都不是成功值,直接跳过
  56 + if (statusCodeResult.StatusCode.Value < 200 || statusCodeResult.StatusCode.Value > 299)
  57 + {
  58 + // 处理规范化结果
  59 + if (!CheckStatusCodeNonUnify(context.HttpContext, out var unifyRes))
  60 + {
  61 + var httpContext = context.HttpContext;
  62 + var statusCode = statusCodeResult.StatusCode.Value;
  63 +
  64 + // 解决刷新 Token 时间和 Token 时间相近问题
  65 + if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
  66 + && httpContext.Response.Headers.ContainsKey("access-token")
  67 + && httpContext.Response.Headers.ContainsKey("x-access-token"))
  68 + {
  69 + httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
  70 + }
  71 +
  72 + // 如果 Response 已经完成输出,则禁止写入
  73 + if (httpContext.Response.HasStarted) return;
  74 + await unifyRes.OnResponseStatusCodes(httpContext, statusCode,
  75 + httpContext.RequestServices.GetService<IOptions<UnifyResultSettingsOptions>>()?.Value);
  76 + }
  77 +
  78 + return;
  79 + }
  80 + }
  81 +
  82 + // 如果出现异常,则不会进入该过滤器
  83 + if (actionExecutedContext.Exception != null) return;
  84 +
  85 + // 获取控制器信息
  86 + var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
  87 +
  88 + // 判断是否支持 MVC 规范化处理,检测配置而已
  89 + // if (!UnifyContext.CheckSupportMvcController(context.HttpContext, actionDescriptor, out _)) return;
  90 +
  91 + // 判断是否跳过规范化处理,检测NonUnifyAttribute而已
  92 + if (CheckSucceededNonUnify(actionDescriptor.MethodInfo))
  93 + {
  94 + return;
  95 + }
  96 + IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
  97 +
  98 + // 处理 BadRequestObjectResult 类型规范化处理
  99 + if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
  100 + {
  101 + // 解析验证消息
  102 + var validationMetadata = GetValidationMetadata(badRequestObjectResult.Value);
  103 +
  104 + var result = unifyResult.OnValidateFailed(context, validationMetadata);
  105 + if (result != null) actionExecutedContext.Result = result;
  106 + }
  107 + else
  108 + {
  109 + IActionResult result = default;
  110 +
  111 + // 检查是否是有效的结果(可进行规范化的结果)
  112 + if (CheckVaildResult(actionExecutedContext.Result, out var data))
  113 + {
  114 + result = unifyResult.OnSucceeded(actionExecutedContext, data);
  115 + }
  116 +
  117 + // 如果是不能规范化的结果类型,则跳过
  118 + if (result == null) return;
  119 +
  120 + actionExecutedContext.Result = result;
  121 + }
  122 + }
  123 +
  124 + /// <summary>
  125 + /// 获取验证错误信息
  126 + /// </summary>
  127 + /// <param name="errors"></param>
  128 + /// <returns></returns>
  129 + private static ValidationMetadata GetValidationMetadata(object errors)
  130 + {
  131 + ModelStateDictionary _modelState = null;
  132 + object validationResults = null;
  133 + (string message, string firstErrorMessage, string firstErrorProperty) = (default, default, default);
  134 +
  135 + // 判断是否是集合类型
  136 + if (errors is IEnumerable && errors is not string)
  137 + {
  138 + // 如果是模型验证字典类型
  139 + if (errors is ModelStateDictionary modelState)
  140 + {
  141 + _modelState = modelState;
  142 + // 将验证错误信息转换成字典并序列化成 Json
  143 + validationResults = modelState.Where(u => modelState[u.Key].ValidationState == ModelValidationState.Invalid)
  144 + .ToDictionary(u => u.Key, u => modelState[u.Key].Errors.Select(c => c.ErrorMessage).ToArray());
  145 + }
  146 + // 如果是 ValidationProblemDetails 特殊类型
  147 + else if (errors is ValidationProblemDetails validation)
  148 + {
  149 + validationResults = validation.Errors
  150 + .ToDictionary(u => u.Key, u => u.Value.ToArray());
  151 + }
  152 + // 如果是字典类型
  153 + else if (errors is Dictionary<string, string[]> dicResults)
  154 + {
  155 + validationResults = dicResults;
  156 + }
  157 +
  158 + message = JsonSerializer.Serialize(validationResults, new JsonSerializerOptions
  159 + {
  160 + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
  161 + WriteIndented = true
  162 + });
  163 + firstErrorMessage = (validationResults as Dictionary<string, string[]>).First().Value[0];
  164 + firstErrorProperty = (validationResults as Dictionary<string, string[]>).First().Key;
  165 + }
  166 + // 其他类型
  167 + else
  168 + {
  169 + validationResults = firstErrorMessage = message = errors?.ToString();
  170 + }
  171 +
  172 + return new ValidationMetadata
  173 + {
  174 + ValidationResult = validationResults,
  175 + Message = message,
  176 + ModelState = _modelState,
  177 + FirstErrorProperty = firstErrorProperty,
  178 + FirstErrorMessage = firstErrorMessage
  179 + };
  180 + }
  181 +
  182 + /// <summary>
  183 + /// 检查是否是有效的结果(可进行规范化的结果)
  184 + /// </summary>
  185 + /// <param name="result"></param>
  186 + /// <param name="data"></param>
  187 + /// <returns></returns>
  188 + private bool CheckVaildResult(IActionResult result, out object data)
  189 + {
  190 + data = default;
  191 +
  192 + // 排除以下结果,跳过规范化处理
  193 + var isDataResult = result switch
  194 + {
  195 + ViewResult => false,
  196 + PartialViewResult => false,
  197 + FileResult => false,
  198 + ChallengeResult => false,
  199 + SignInResult => false,
  200 + SignOutResult => false,
  201 + RedirectToPageResult => false,
  202 + RedirectToRouteResult => false,
  203 + RedirectResult => false,
  204 + RedirectToActionResult => false,
  205 + LocalRedirectResult => false,
  206 + ForbidResult => false,
  207 + ViewComponentResult => false,
  208 + PageResult => false,
  209 + NotFoundResult => false,
  210 + NotFoundObjectResult => false,
  211 + _ => true,
  212 + };
  213 +
  214 + // 目前支持返回值 ActionResult
  215 + if (isDataResult) data = result switch
  216 + {
  217 + // 处理内容结果
  218 + ContentResult content => content.Content,
  219 + // 处理对象结果
  220 + ObjectResult obj => obj.Value,
  221 + // 处理 JSON 对象
  222 + JsonResult json => json.Value,
  223 + _ => null,
  224 + };
  225 +
  226 + return isDataResult;
  227 + }
  228 +
  229 +
  230 + /// <summary>
  231 + /// 检查短路状态码(>=400)是否进行规范化处理
  232 + /// </summary>
  233 + /// <param name="context"></param>
  234 + /// <param name="unifyResult"></param>
  235 + /// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
  236 + internal static bool CheckStatusCodeNonUnify(HttpContext context, out IUnifyResultProvider unifyResult)
  237 + {
  238 + // 获取终点路由特性
  239 + var endpointFeature = context.Features.Get<IEndpointFeature>();
  240 + if (endpointFeature == null) return (unifyResult = null) == null;
  241 +
  242 + // 判断是否跳过规范化处理
  243 + var isSkip = context.GetEndpoint()?.Metadata?.GetMetadata<NonUnifyAttribute>()!= null
  244 + || endpointFeature?.Endpoint?.Metadata?.GetMetadata<NonUnifyAttribute>() != null
  245 + || context.Request.Headers["accept"].ToString().Contains("odata.metadata=", StringComparison.OrdinalIgnoreCase)
  246 + || context.Request.Headers["accept"].ToString().Contains("odata.streaming=", StringComparison.OrdinalIgnoreCase);
  247 +
  248 + if (isSkip == true) unifyResult = null;
  249 + else
  250 + {
  251 + unifyResult = context.RequestServices.GetRequiredService<IUnifyResultProvider>();
  252 + }
  253 +
  254 + return unifyResult == null || isSkip;
  255 + }
  256 +
  257 + /// <summary>
  258 + /// 检查请求成功是否进行规范化处理
  259 + /// </summary>
  260 + /// <param name="method"></param>
  261 + /// <param name="isWebRequest"></param>
  262 + /// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
  263 + private bool CheckSucceededNonUnify(MethodInfo method, bool isWebRequest = true)
  264 + {
  265 + // 判断是否跳过规范化处理
  266 + var isSkip = method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType) || typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType))
  267 + || method.ReflectedType.IsDefined(typeof(NonUnifyAttribute), true)
  268 + || method.DeclaringType.Assembly.GetName().Name.StartsWith("Microsoft.AspNetCore.OData");
  269 +
  270 + if (!isWebRequest)
  271 + {
  272 + return isSkip;
  273 + }
  274 + return isSkip;
  275 + }
  276 +}
0 277 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/IUnifyResultProvider.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/IUnifyResultProvider.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +using Microsoft.AspNetCore.Http;
  16 +using Microsoft.AspNetCore.Mvc;
  17 +using Microsoft.AspNetCore.Mvc.Filters;
  18 +
  19 +namespace Yi.Framework.AspNetCore.UnifyResult;
  20 +
  21 +/// <summary>
  22 +/// 规范化结果提供器
  23 +/// </summary>
  24 +public interface IUnifyResultProvider
  25 +{
  26 + /// <summary>
  27 + /// 异常返回值
  28 + /// </summary>
  29 + /// <param name="context"></param>
  30 + /// <param name="metadata"></param>
  31 + /// <returns></returns>
  32 + IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata);
  33 +
  34 + /// <summary>
  35 + /// 成功返回值
  36 + /// </summary>
  37 + /// <param name="context"></param>
  38 + /// <param name="data"></param>
  39 + /// <returns></returns>
  40 + IActionResult OnSucceeded(ActionExecutedContext context, object data);
  41 +
  42 + /// <summary>
  43 + /// 验证失败返回值
  44 + /// </summary>
  45 + /// <param name="context"></param>
  46 + /// <param name="metadata"></param>
  47 + /// <returns></returns>
  48 + IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata);
  49 +
  50 + /// <summary>
  51 + /// 拦截返回状态码
  52 + /// </summary>
  53 + /// <param name="context"></param>
  54 + /// <param name="statusCode"></param>
  55 + /// <param name="unifyResultSettings"></param>
  56 + /// <returns></returns>
  57 + Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = default);
  58 +}
0 59 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/NonUnifyAttribute.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/NonUnifyAttribute.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +namespace Yi.Framework.AspNetCore.UnifyResult;
  16 +
  17 +/// <summary>
  18 +/// 禁止规范化处理
  19 +/// </summary>
  20 +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
  21 +public sealed class NonUnifyAttribute : Attribute
  22 +{
  23 +}
0 24 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Providers/RESTfulResultProvider.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Providers/RESTfulResultProvider.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +using Microsoft.AspNetCore.Http;
  16 +using Microsoft.AspNetCore.Mvc;
  17 +using Microsoft.AspNetCore.Mvc.Filters;
  18 +using Volo.Abp.DependencyInjection;
  19 +
  20 +namespace Yi.Framework.AspNetCore.UnifyResult.Providers;
  21 +
  22 +/// <summary>
  23 +/// RESTful 风格返回值
  24 +/// </summary>
  25 +[Dependency(TryRegister = true)]
  26 +[ExposeServices(typeof(IUnifyResultProvider))]
  27 +public class RESTfulResultProvider : IUnifyResultProvider,ITransientDependency
  28 +{
  29 + /// <summary>
  30 + /// 设置响应状态码
  31 + /// </summary>
  32 + /// <param name="context"></param>
  33 + /// <param name="statusCode"></param>
  34 + /// <param name="unifyResultSettings"></param>
  35 + public static void SetResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
  36 + {
  37 + if (unifyResultSettings == null) return;
  38 +
  39 + // 篡改响应状态码
  40 + if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0)
  41 + {
  42 + var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
  43 + if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0)
  44 + {
  45 + context.Response.StatusCode = adaptStatusCode[1];
  46 + return;
  47 + }
  48 + }
  49 +
  50 + // 如果为 null,则所有请求错误的状态码设置为 200
  51 + if (unifyResultSettings.Return200StatusCodes == null) context.Response.StatusCode = 200;
  52 + // 否则只有里面的才设置为 200
  53 + else if (unifyResultSettings.Return200StatusCodes.Contains(statusCode)) context.Response.StatusCode = 200;
  54 + else { }
  55 + }
  56 +
  57 + /// <summary>
  58 + /// 异常返回值
  59 + /// </summary>
  60 + /// <param name="context"></param>
  61 + /// <param name="metadata"></param>
  62 + /// <returns></returns>
  63 + public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
  64 + {
  65 + return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
  66 + }
  67 +
  68 + /// <summary>
  69 + /// 成功返回值
  70 + /// </summary>
  71 + /// <param name="context"></param>
  72 + /// <param name="data"></param>
  73 + /// <returns></returns>
  74 + public IActionResult OnSucceeded(ActionExecutedContext context, object data)
  75 + {
  76 + return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
  77 + }
  78 +
  79 + /// <summary>
  80 + /// 验证失败/业务异常返回值
  81 + /// </summary>
  82 + /// <param name="context"></param>
  83 + /// <param name="metadata"></param>
  84 + /// <returns></returns>
  85 + public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
  86 + {
  87 + return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
  88 + }
  89 +
  90 + /// <summary>
  91 + /// 特定状态码返回值
  92 + /// </summary>
  93 + /// <param name="context"></param>
  94 + /// <param name="statusCode"></param>
  95 + /// <param name="unifyResultSettings"></param>
  96 + /// <returns></returns>
  97 + public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
  98 + {
  99 + // 设置响应状态码
  100 + SetResponseStatusCodes(context, statusCode, unifyResultSettings);
  101 +
  102 + switch (statusCode)
  103 + {
  104 + // 处理 401 状态码
  105 + case StatusCodes.Status401Unauthorized:
  106 + await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized"));
  107 + break;
  108 + // 处理 403 状态码
  109 + case StatusCodes.Status403Forbidden:
  110 + await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden"));
  111 + break;
  112 +
  113 + default: break;
  114 + }
  115 + }
  116 +
  117 + /// <summary>
  118 + /// 返回 RESTful 风格结果集
  119 + /// </summary>
  120 + /// <param name="statusCode"></param>
  121 + /// <param name="succeeded"></param>
  122 + /// <param name="data"></param>
  123 + /// <param name="errors"></param>
  124 + /// <returns></returns>
  125 + public static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
  126 + {
  127 + return new RESTfulResult<object>
  128 + {
  129 + StatusCode = statusCode,
  130 + Succeeded = succeeded,
  131 + Data = data,
  132 + Errors = errors,
  133 + Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
  134 + };
  135 + }
  136 +}
0 137 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/RESTfulResult.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/RESTfulResult.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +namespace Yi.Framework.AspNetCore.UnifyResult;
  16 +
  17 +/// <summary>
  18 +/// RESTful 风格结果集
  19 +/// </summary>
  20 +/// <typeparam name="T"></typeparam>
  21 +public class RESTfulResult<T>
  22 +{
  23 + /// <summary>
  24 + /// 状态码
  25 + /// </summary>
  26 + public int? StatusCode { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 数据
  30 + /// </summary>
  31 + public T Data { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 执行成功
  35 + /// </summary>
  36 + public bool Succeeded { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 错误信息
  40 + /// </summary>
  41 + public object Errors { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 附加数据
  45 + /// </summary>
  46 + public object Extras { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 时间戳
  50 + /// </summary>
  51 + public long Timestamp { get; set; }
  52 +}
0 53 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/UnifyResultExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/UnifyResultExtensions.cs
  1 +using Microsoft.AspNetCore.Mvc;
  2 +using Microsoft.Extensions.DependencyInjection;
  3 +using Swashbuckle.AspNetCore.SwaggerGen;
  4 +using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
  5 +using Volo.Abp.AspNetCore.Mvc.Response;
  6 +using Yi.Framework.AspNetCore.UnifyResult.Fiters;
  7 +
  8 +namespace Yi.Framework.AspNetCore.UnifyResult;
  9 +
  10 +/// <summary>
  11 +/// 规范化接口
  12 +/// 由于太多人反应,想兼容一套类似furion的返回情况,200状态码包一层更符合国内习惯,既然如此,不如直接搬过来
  13 +/// </summary>
  14 +public static class UnifyResultExtensions
  15 +{
  16 + public static IServiceCollection AddFurionUnifyResultApi(this IServiceCollection services)
  17 + {
  18 + //成功规范接口
  19 + services.AddTransient<SucceededUnifyResultFilter>();
  20 + //异常规范接口
  21 + services.AddTransient<FriendlyExceptionFilter>();
  22 + services.AddMvc(options =>
  23 + {
  24 + options.Filters.RemoveAll(x => (x as ServiceFilterAttribute)?.ServiceType == typeof(AbpExceptionFilter));
  25 + options.Filters.RemoveAll(x => (x as ServiceFilterAttribute)?.ServiceType == typeof(AbpNoContentActionFilter));
  26 + options.Filters.AddService<SucceededUnifyResultFilter>(99);
  27 + options.Filters.AddService<FriendlyExceptionFilter>(100);
  28 + });
  29 + return services;
  30 + }
  31 +}
0 32 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/UnifyResultSettingsOptions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/UnifyResultSettingsOptions.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +using Microsoft.Extensions.Configuration;
  16 +
  17 +namespace Yi.Framework.AspNetCore.UnifyResult;
  18 +
  19 +/// <summary>
  20 +/// 规范化配置选项
  21 +/// </summary>
  22 +public sealed class UnifyResultSettingsOptions
  23 +{
  24 + /// <summary>
  25 + /// 设置返回 200 状态码列表
  26 + /// <para>默认:401,403,如果设置为 null,则标识所有状态码都返回 200 </para>
  27 + /// </summary>
  28 + public int[] Return200StatusCodes { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 适配(篡改)Http 状态码(只支持短路状态码,比如 401,403,500 等)
  32 + /// </summary>
  33 + public int[][] AdaptStatusCodes { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 是否支持 MVC 控制台规范化处理
  37 + /// </summary>
  38 + public bool? SupportMvcController { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 选项后期配置
  42 + /// </summary>
  43 + /// <param name="options"></param>
  44 + /// <param name="configuration"></param>
  45 + public void PostConfigure(UnifyResultSettingsOptions options, IConfiguration configuration)
  46 + {
  47 + options.Return200StatusCodes ??= new[] { 401, 403 };
  48 + options.SupportMvcController ??= false;
  49 + }
  50 +}
0 51 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ValidationMetadata.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ValidationMetadata.cs
  1 +// MIT 许可证
  2 +//
  3 +// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
  4 +//
  5 +// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
  6 +// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
  7 +// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
  8 +//
  9 +// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
  10 +//
  11 +// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
  12 +// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
  13 +// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
  14 +
  15 +using Microsoft.AspNetCore.Mvc.ModelBinding;
  16 +
  17 +namespace Yi.Framework.AspNetCore.UnifyResult;
  18 +
  19 +/// <summary>
  20 +/// 验证信息元数据
  21 +/// </summary>
  22 +public sealed class ValidationMetadata
  23 +{
  24 + /// <summary>
  25 + /// 验证结果
  26 + /// </summary>
  27 + /// <remarks>返回字典或字符串类型</remarks>
  28 + public object ValidationResult { get; internal set; }
  29 +
  30 + /// <summary>
  31 + /// 异常消息
  32 + /// </summary>
  33 + public string Message { get; internal set; }
  34 +
  35 + /// <summary>
  36 + /// 验证状态
  37 + /// </summary>
  38 + public ModelStateDictionary ModelState { get; internal set; }
  39 +
  40 + /// <summary>
  41 + /// 错误码
  42 + /// </summary>
  43 + public object ErrorCode { get; internal set; }
  44 +
  45 + /// <summary>
  46 + /// 错误码(没被复写过的 ErrorCode )
  47 + /// </summary>
  48 + public object OriginErrorCode { get; internal set; }
  49 +
  50 + /// <summary>
  51 + /// 状态码
  52 + /// </summary>
  53 + public int? StatusCode { get; internal set; }
  54 +
  55 + /// <summary>
  56 + /// 首个错误属性
  57 + /// </summary>
  58 + public string FirstErrorProperty { get; internal set; }
  59 +
  60 + /// <summary>
  61 + /// 首个错误消息
  62 + /// </summary>
  63 + public string FirstErrorMessage { get; internal set; }
  64 +
  65 + /// <summary>
  66 + /// 额外数据
  67 + /// </summary>
  68 + public object Data { get; internal set; }
  69 +}
0 70 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Yi.Framework.AspNetCore.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Yi.Framework.AspNetCore.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 + <Import Project="..\..\common.props" />
  3 +
  4 + <ItemGroup>
  5 + <Compile Remove="Cors\**" />
  6 + <EmbeddedResource Remove="Cors\**" />
  7 + <None Remove="Cors\**" />
  8 + </ItemGroup>
  9 +
  10 + <ItemGroup>
  11 + <PackageReference Include="Volo.Abp.Json" Version="$(AbpVersion)" />
  12 + <PackageReference Include="Volo.Abp.Swashbuckle" Version="$(AbpVersion)" />
  13 + </ItemGroup>
  14 +
  15 + <ItemGroup>
  16 + <ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
  17 + </ItemGroup>
  18 +
  19 +</Project>
... ...
Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/YiFrameworkAspNetCoreModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/YiFrameworkAspNetCoreModule.cs
  1 +using System.Reflection;
  2 +using Microsoft.AspNetCore.Builder;
  3 +using Microsoft.AspNetCore.Mvc;
  4 +using Microsoft.AspNetCore.Mvc.ApiExplorer;
  5 +using Microsoft.AspNetCore.Mvc.Controllers;
  6 +using Microsoft.Extensions.DependencyInjection;
  7 +using Microsoft.Extensions.DependencyInjection.Extensions;
  8 +using Microsoft.Extensions.Options;
  9 +using Microsoft.OpenApi.Models;
  10 +using Newtonsoft.Json.Linq;
  11 +using Swashbuckle.AspNetCore.SwaggerGen;
  12 +using Volo.Abp;
  13 +using Volo.Abp.AspNetCore.Mvc;
  14 +using Volo.Abp.AspNetCore.WebClientInfo;
  15 +using Volo.Abp.DependencyInjection;
  16 +using Volo.Abp.Modularity;
  17 +using Yi.Framework.AspNetCore.Mvc;
  18 +using Yi.Framework.Core;
  19 +
  20 +namespace Yi.Framework.AspNetCore
  21 +{
  22 + /// <summary>
  23 + /// Yi框架ASP.NET Core模块
  24 + /// </summary>
  25 + [DependsOn(typeof(YiFrameworkCoreModule))]
  26 + public class YiFrameworkAspNetCoreModule : AbpModule
  27 + {
  28 + /// <summary>
  29 + /// 配置服务后的处理
  30 + /// </summary>
  31 + public override void PostConfigureServices(ServiceConfigurationContext context)
  32 + {
  33 + var services = context.Services;
  34 +
  35 + // 替换默认的WebClientInfoProvider为支持代理的实现
  36 + services.Replace(new ServiceDescriptor(
  37 + typeof(IWebClientInfoProvider),
  38 + typeof(RealIpHttpContextWebClientInfoProvider),
  39 + ServiceLifetime.Transient));
  40 + }
  41 + }
  42 +}
0 43 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/UnitOfWorkHangfireFilter.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/UnitOfWorkHangfireFilter.cs
  1 +using Hangfire.Server;
  2 +using Volo.Abp.DependencyInjection;
  3 +using Volo.Abp.Threading;
  4 +using Volo.Abp.Uow;
  5 +
  6 +namespace Yi.Framework.BackgroundWorkers.Hangfire;
  7 +
  8 +/// <summary>
  9 +/// Hangfire 工作单元过滤器
  10 +/// 用于管理后台任务的事务处理
  11 +/// </summary>
  12 +public sealed class UnitOfWorkHangfireFilter : IServerFilter, ISingletonDependency
  13 +{
  14 + private const string UnitOfWorkItemKey = "HangfireUnitOfWork";
  15 + private readonly IUnitOfWorkManager _unitOfWorkManager;
  16 +
  17 + /// <summary>
  18 + /// 初始化工作单元过滤器
  19 + /// </summary>
  20 + /// <param name="unitOfWorkManager">工作单元管理器</param>
  21 + public UnitOfWorkHangfireFilter(IUnitOfWorkManager unitOfWorkManager)
  22 + {
  23 + _unitOfWorkManager = unitOfWorkManager;
  24 + }
  25 +
  26 + /// <summary>
  27 + /// 任务执行前的处理
  28 + /// </summary>
  29 + /// <param name="context">执行上下文</param>
  30 + public void OnPerforming(PerformingContext context)
  31 + {
  32 + // 开启一个工作单元并存储到上下文中
  33 + var uow = _unitOfWorkManager.Begin();
  34 + context.Items.Add(UnitOfWorkItemKey, uow);
  35 + }
  36 +
  37 + /// <summary>
  38 + /// 任务执行后的处理
  39 + /// </summary>
  40 + /// <param name="context">执行上下文</param>
  41 + public void OnPerformed(PerformedContext context)
  42 + {
  43 + AsyncHelper.RunSync(() => OnPerformedAsync(context));
  44 + }
  45 +
  46 + /// <summary>
  47 + /// 任务执行后的异步处理
  48 + /// </summary>
  49 + /// <param name="context">执行上下文</param>
  50 + private async Task OnPerformedAsync(PerformedContext context)
  51 + {
  52 + if (!context.Items.TryGetValue(UnitOfWorkItemKey, out var obj) ||
  53 + obj is not IUnitOfWork uow)
  54 + {
  55 + return;
  56 + }
  57 +
  58 + try
  59 + {
  60 + // 如果没有异常且工作单元未完成,则提交事务
  61 + if (context.Exception == null && !uow.IsCompleted)
  62 + {
  63 + await uow.CompleteAsync();
  64 + }
  65 + else
  66 + {
  67 + // 否则回滚事务
  68 + await uow.RollbackAsync();
  69 + }
  70 + }
  71 + finally
  72 + {
  73 + // 确保工作单元被释放
  74 + uow.Dispose();
  75 + }
  76 + }
  77 +}
0 78 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/Yi.Framework.BackgroundWorkers.Hangfire.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/Yi.Framework.BackgroundWorkers.Hangfire.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 +
  3 + <Import Project="..\..\common.props" />
  4 +
  5 + <PropertyGroup>
  6 + <TargetFramework>net8.0</TargetFramework>
  7 + <ImplicitUsings>enable</ImplicitUsings>
  8 + <Nullable>enable</Nullable>
  9 + </PropertyGroup>
  10 +
  11 +
  12 + <ItemGroup>
  13 + <PackageReference Include="Volo.Abp.BackgroundJobs.Hangfire" Version="$(AbpVersion)" />
  14 + <PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" Version="$(AbpVersion)" />
  15 + </ItemGroup>
  16 +
  17 +
  18 +</Project>
... ...
Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiFrameworkBackgroundWorkersHangfireModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiFrameworkBackgroundWorkersHangfireModule.cs
  1 +using System.Linq.Expressions;
  2 +using Hangfire;
  3 +using Microsoft.Extensions.Configuration;
  4 +using Microsoft.Extensions.DependencyInjection;
  5 +using Volo.Abp.BackgroundJobs.Hangfire;
  6 +using Volo.Abp.BackgroundWorkers;
  7 +using Volo.Abp.BackgroundWorkers.Hangfire;
  8 +using Volo.Abp.DynamicProxy;
  9 +
  10 +namespace Yi.Framework.BackgroundWorkers.Hangfire;
  11 +
  12 +/// <summary>
  13 +/// Hangfire 后台任务模块
  14 +/// </summary>
  15 +[DependsOn(typeof(AbpBackgroundWorkersHangfireModule),
  16 + typeof(AbpBackgroundJobsHangfireModule))]
  17 +public sealed class YiFrameworkBackgroundWorkersHangfireModule : AbpModule
  18 +{
  19 + /// <summary>
  20 + /// 配置服务前的预处理
  21 + /// </summary>
  22 + /// <param name="context">服务配置上下文</param>
  23 + public override void PreConfigureServices(ServiceConfigurationContext context)
  24 + {
  25 + // 添加 Hangfire 后台任务约定注册器
  26 + context.Services.AddConventionalRegistrar(new YiHangfireConventionalRegistrar());
  27 + }
  28 +
  29 + /// <summary>
  30 + /// 应用程序初始化
  31 + /// </summary>
  32 + /// <param name="context">应用程序初始化上下文</param>
  33 + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
  34 + {
  35 + // 获取后台任务管理器和所有 Hangfire 后台任务
  36 + var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
  37 + var workers = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
  38 +
  39 + // 获取配置
  40 + var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
  41 +
  42 + // 检查是否启用 Redis
  43 + var isRedisEnabled = configuration.GetValue<bool>("Redis:IsEnabled");
  44 +
  45 + foreach (var worker in workers)
  46 + {
  47 + // 设置时区为本地时区(上海)
  48 + worker.TimeZone = TimeZoneInfo.Local;
  49 +
  50 + if (isRedisEnabled)
  51 + {
  52 + // Redis 模式:使用 ABP 后台任务管理器
  53 + await backgroundWorkerManager.AddAsync(worker);
  54 + }
  55 + else
  56 + {
  57 + // 内存模式:直接使用 Hangfire
  58 + var unProxyWorker = ProxyHelper.UnProxy(worker);
  59 +
  60 + // 添加或更新循环任务
  61 + RecurringJob.AddOrUpdate(
  62 + worker.RecurringJobId,
  63 + (Expression<Func<Task>>)(() =>
  64 + ((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(default)),
  65 + worker.CronExpression,
  66 + new RecurringJobOptions
  67 + {
  68 + TimeZone = worker.TimeZone
  69 + });
  70 + }
  71 + }
  72 + }
  73 +
  74 + /// <summary>
  75 + /// 应用程序初始化前的预处理
  76 + /// </summary>
  77 + /// <param name="context">应用程序初始化上下文</param>
  78 + public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
  79 + {
  80 + // 添加工作单元过滤器
  81 + var services = context.ServiceProvider;
  82 + GlobalJobFilters.Filters.Add(services.GetRequiredService<UnitOfWorkHangfireFilter>());
  83 + }
  84 +}
0 85 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiHangfireConventionalRegistrar.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiHangfireConventionalRegistrar.cs
  1 +using Volo.Abp.BackgroundWorkers.Hangfire;
  2 +using Volo.Abp.DependencyInjection;
  3 +
  4 +namespace Yi.Framework.BackgroundWorkers.Hangfire;
  5 +
  6 +/// <summary>
  7 +/// Hangfire 后台任务约定注册器
  8 +/// </summary>
  9 +public sealed class YiHangfireConventionalRegistrar : DefaultConventionalRegistrar
  10 +{
  11 + /// <summary>
  12 + /// 检查类型是否禁用约定注册
  13 + /// </summary>
  14 + /// <param name="type">要检查的类型</param>
  15 + /// <returns>如果类型不是 IHangfireBackgroundWorker 或已被禁用则返回 true</returns>
  16 + protected override bool IsConventionalRegistrationDisabled(Type type)
  17 + {
  18 + return !typeof(IHangfireBackgroundWorker).IsAssignableFrom(type) ||
  19 + base.IsConventionalRegistrationDisabled(type);
  20 + }
  21 +
  22 + /// <summary>
  23 + /// 获取要暴露的服务类型列表
  24 + /// </summary>
  25 + /// <param name="type">实现类型</param>
  26 + /// <returns>服务类型列表</returns>
  27 + protected override List<Type> GetExposedServiceTypes(Type type)
  28 + {
  29 + return new List<Type>
  30 + {
  31 + typeof(IHangfireBackgroundWorker)
  32 + };
  33 + }
  34 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiTokenAuthorizationFilter.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.BackgroundWorkers.Hangfire/YiTokenAuthorizationFilter.cs
  1 +using Hangfire.Dashboard;
  2 +using Microsoft.AspNetCore.Http;
  3 +using Microsoft.Extensions.DependencyInjection;
  4 +using Volo.Abp.DependencyInjection;
  5 +using Volo.Abp.Users;
  6 +
  7 +namespace Yi.Framework.BackgroundWorkers.Hangfire;
  8 +
  9 +/// <summary>
  10 +/// Hangfire 仪表盘的令牌认证过滤器
  11 +/// </summary>
  12 +public sealed class YiTokenAuthorizationFilter : IDashboardAsyncAuthorizationFilter, ITransientDependency
  13 +{
  14 + private const string BearerPrefix = "Bearer ";
  15 + private const string TokenCookieKey = "Token";
  16 + private const string HtmlContentType = "text/html";
  17 +
  18 + private readonly IServiceProvider _serviceProvider;
  19 + private string _requiredUsername = "cc";
  20 + private TimeSpan _tokenExpiration = TimeSpan.FromMinutes(10);
  21 +
  22 + /// <summary>
  23 + /// 初始化令牌认证过滤器
  24 + /// </summary>
  25 + /// <param name="serviceProvider">服务提供者</param>
  26 + public YiTokenAuthorizationFilter(IServiceProvider serviceProvider)
  27 + {
  28 + _serviceProvider = serviceProvider;
  29 + }
  30 +
  31 + /// <summary>
  32 + /// 设置需要的用户名
  33 + /// </summary>
  34 + /// <param name="username">允许访问的用户名</param>
  35 + /// <returns>当前实例,支持链式调用</returns>
  36 + public YiTokenAuthorizationFilter SetRequiredUsername(string username)
  37 + {
  38 + _requiredUsername = username ?? throw new ArgumentNullException(nameof(username));
  39 + return this;
  40 + }
  41 +
  42 + /// <summary>
  43 + /// 设置令牌过期时间
  44 + /// </summary>
  45 + /// <param name="expiration">过期时间间隔</param>
  46 + /// <returns>当前实例,支持链式调用</returns>
  47 + public YiTokenAuthorizationFilter SetTokenExpiration(TimeSpan expiration)
  48 + {
  49 + _tokenExpiration = expiration;
  50 + return this;
  51 + }
  52 +
  53 + /// <summary>
  54 + /// 授权验证
  55 + /// </summary>
  56 + /// <param name="context">仪表盘上下文</param>
  57 + /// <returns>是否通过授权</returns>
  58 + public bool Authorize(DashboardContext context)
  59 + {
  60 + var httpContext = context.GetHttpContext();
  61 + var currentUser = _serviceProvider.GetRequiredService<ICurrentUser>();
  62 +
  63 + if (!currentUser.IsAuthenticated)
  64 + {
  65 + SetChallengeResponse(httpContext);
  66 + return false;
  67 + }
  68 +
  69 + // 如果验证通过,设置 cookie
  70 + var authorization = httpContext.Request.Headers.Authorization.ToString();
  71 + if (!string.IsNullOrWhiteSpace(authorization) && authorization.StartsWith(BearerPrefix))
  72 + {
  73 + var token = authorization[BearerPrefix.Length..];
  74 + SetTokenCookie(httpContext, token);
  75 + }
  76 +
  77 + return currentUser.UserName == _requiredUsername;
  78 + }
  79 +
  80 + /// <summary>
  81 + /// 设置认证挑战响应
  82 + /// 当用户未认证时,返回一个包含令牌输入表单的HTML页面
  83 + /// </summary>
  84 + /// <param name="httpContext">HTTP 上下文</param>
  85 + private void SetChallengeResponse(HttpContext httpContext)
  86 + {
  87 + httpContext.Response.StatusCode = 401;
  88 + httpContext.Response.ContentType = HtmlContentType;
  89 +
  90 + var html = @"
  91 + <html>
  92 + <head>
  93 + <title>Hangfire Dashboard Authorization</title>
  94 + <style>
  95 + body { font-family: Arial, sans-serif; margin: 40px; }
  96 + .container { max-width: 400px; margin: 0 auto; }
  97 + .form-group { margin-bottom: 15px; }
  98 + input[type='text'] { width: 100%; padding: 8px; }
  99 + button { background: #337ab7; color: white; border: none; padding: 10px 15px; cursor: pointer; }
  100 + button:hover { background: #286090; }
  101 + </style>
  102 + </head>
  103 + <body>
  104 + <div class='container'>
  105 + <h2>Authorization Required</h2>
  106 + <div class='form-group'>
  107 + <input type='text' id='token' placeholder='Enter your Bearer token...' />
  108 + </div>
  109 + <button onclick='authorize()'>Authorize</button>
  110 + </div>
  111 + <script>
  112 + function authorize() {
  113 + var token = document.getElementById('token').value;
  114 + if (token) {
  115 + document.cookie = 'Token=' + token + '; path=/';
  116 + window.location.reload();
  117 + }
  118 + }
  119 + </script>
  120 + </body>
  121 + </html>";
  122 +
  123 + httpContext.Response.WriteAsync(html);
  124 + }
  125 +
  126 + /// <summary>
  127 + /// 设置令牌 Cookie
  128 + /// </summary>
  129 + /// <param name="httpContext">HTTP 上下文</param>
  130 + /// <param name="token">令牌值</param>
  131 + private void SetTokenCookie(HttpContext httpContext, string token)
  132 + {
  133 + var cookieOptions = new CookieOptions
  134 + {
  135 + Expires = DateTimeOffset.Now.Add(_tokenExpiration),
  136 + HttpOnly = true,
  137 + Secure = httpContext.Request.IsHttps,
  138 + SameSite = SameSiteMode.Lax
  139 + };
  140 +
  141 + httpContext.Response.Cookies.Append(TokenCookieKey, token, cookieOptions);
  142 + }
  143 +
  144 + public Task<bool> AuthorizeAsync(DashboardContext context)
  145 + {
  146 + return Task.FromResult(Authorize(context));
  147 + }
  148 +}
0 149 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/FreeSqlOptions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/FreeSqlOptions.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +using FreeRedis;
  7 +
  8 +namespace Yi.Framework.Caching.FreeRedis
  9 +{
  10 + /// <summary>
  11 + /// 便于转到定义
  12 + /// </summary>
  13 + public class FreeSqlOptions: ConnectionStringBuilder
  14 + {
  15 +
  16 + }
  17 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/Yi.Framework.Caching.FreeRedis.csproj 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/Yi.Framework.Caching.FreeRedis.csproj
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 + <Import Project="..\..\common.props" />
  3 +
  4 + <PropertyGroup>
  5 + <TargetFramework>net8.0</TargetFramework>
  6 + <ImplicitUsings>enable</ImplicitUsings>
  7 + <Nullable>enable</Nullable>
  8 + </PropertyGroup>
  9 +
  10 + <ItemGroup>
  11 + <PackageReference Include="FreeRedis" Version="1.2.14" />
  12 + <PackageReference Include="FreeRedis.DistributedCache" Version="1.2.5" />
  13 + <PackageReference Include="Volo.Abp.Caching" Version="$(AbpVersion)" />
  14 + </ItemGroup>
  15 +
  16 +</Project>
... ...
Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/YiDistributedCacheKeyNormalizer.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/YiDistributedCacheKeyNormalizer.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +using Microsoft.Extensions.Options;
  7 +using Volo.Abp.Caching;
  8 +using Volo.Abp.DependencyInjection;
  9 +using Volo.Abp.MultiTenancy;
  10 +
  11 +namespace Yi.Framework.Caching.FreeRedis
  12 +{
  13 + /// <summary>
  14 + /// 缓存键标准化处理器
  15 + /// 用于处理缓存键的格式化和多租户支持
  16 + /// </summary>
  17 + [Dependency(ReplaceServices = true)]
  18 + public class YiDistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency
  19 + {
  20 + private readonly ICurrentTenant _currentTenant;
  21 + private readonly AbpDistributedCacheOptions _distributedCacheOptions;
  22 +
  23 + /// <summary>
  24 + /// 构造函数
  25 + /// </summary>
  26 + /// <param name="currentTenant">当前租户服务</param>
  27 + /// <param name="distributedCacheOptions">分布式缓存配置选项</param>
  28 + public YiDistributedCacheKeyNormalizer(
  29 + ICurrentTenant currentTenant,
  30 + IOptions<AbpDistributedCacheOptions> distributedCacheOptions)
  31 + {
  32 + _currentTenant = currentTenant;
  33 + _distributedCacheOptions = distributedCacheOptions.Value;
  34 + }
  35 +
  36 + /// <summary>
  37 + /// 标准化缓存键
  38 + /// </summary>
  39 + /// <param name="args">缓存键标准化参数</param>
  40 + /// <returns>标准化后的缓存键</returns>
  41 + public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args)
  42 + {
  43 + // 添加全局缓存前缀
  44 + var normalizedKey = $"{_distributedCacheOptions.KeyPrefix}{args.Key}";
  45 +
  46 + //todo 多租户支持已注释,如需启用取消注释即可
  47 + //if (!args.IgnoreMultiTenancy && _currentTenant.Id.HasValue)
  48 + //{
  49 + // normalizedKey = $"t:{_currentTenant.Id.Value},{normalizedKey}";
  50 + //}
  51 +
  52 + return normalizedKey;
  53 + }
  54 + }
  55 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/YiFrameworkCachingFreeRedisModule.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Caching.FreeRedis/YiFrameworkCachingFreeRedisModule.cs
  1 +using FreeRedis;
  2 +using Microsoft.Extensions.Caching.Distributed;
  3 +using Microsoft.Extensions.Configuration;
  4 +using Microsoft.Extensions.DependencyInjection;
  5 +using Microsoft.Extensions.DependencyInjection.Extensions;
  6 +using Volo.Abp.Caching;
  7 +
  8 +namespace Yi.Framework.Caching.FreeRedis
  9 +{
  10 + /// <summary>
  11 + /// FreeRedis缓存模块
  12 + /// 提供基于FreeRedis的分布式缓存实现
  13 + /// </summary>
  14 + [DependsOn(typeof(AbpCachingModule))]
  15 + public class YiFrameworkCachingFreeRedisModule : AbpModule
  16 + {
  17 + private const string RedisEnabledKey = "Redis:IsEnabled";
  18 + private const string RedisConfigurationKey = "Redis:Configuration";
  19 +
  20 + /// <summary>
  21 + /// 配置服务
  22 + /// </summary>
  23 + /// <param name="context">服务配置上下文</param>
  24 + public override void ConfigureServices(ServiceConfigurationContext context)
  25 + {
  26 + var configuration = context.Services.GetConfiguration();
  27 +
  28 + // 检查Redis是否启用
  29 + if (!IsRedisEnabled(configuration))
  30 + {
  31 + return;
  32 + }
  33 +
  34 + // 注册Redis服务
  35 + RegisterRedisServices(context, configuration);
  36 + }
  37 +
  38 + /// <summary>
  39 + /// 检查Redis是否启用
  40 + /// </summary>
  41 + /// <param name="configuration">配置</param>
  42 + /// <returns>是否启用Redis</returns>
  43 + private static bool IsRedisEnabled(IConfiguration configuration)
  44 + {
  45 + var redisEnabled = configuration[RedisEnabledKey];
  46 + return redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled);
  47 + }
  48 +
  49 + /// <summary>
  50 + /// 注册Redis相关服务
  51 + /// </summary>
  52 + /// <param name="context">服务配置上下文</param>
  53 + /// <param name="configuration">配置</param>
  54 + private static void RegisterRedisServices(ServiceConfigurationContext context, IConfiguration configuration)
  55 + {
  56 + var redisConfiguration = configuration[RedisConfigurationKey];
  57 + var redisClient = new RedisClient(redisConfiguration);
  58 +
  59 + context.Services.AddSingleton<IRedisClient>(redisClient);
  60 + context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache>(
  61 + new DistributedCache(redisClient)));
  62 + }
  63 + }
  64 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Data/IOrderNum.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Data/IOrderNum.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Data
  8 +{
  9 + /// <summary>
  10 + /// 排序接口
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 实现此接口的实体类将支持排序功能
  14 + /// 通常用于列表数据的展示顺序控制
  15 + /// </remarks>
  16 + public interface IOrderNum
  17 + {
  18 + /// <summary>
  19 + /// 排序号
  20 + /// </summary>
  21 + /// <remarks>
  22 + /// 数字越小越靠前,默认为0
  23 + /// </remarks>
  24 + int OrderNum { get; set; }
  25 + }
  26 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Data/IState.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Data/IState.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Data
  8 +{
  9 + /// <summary>
  10 + /// 状态接口
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 实现此接口的实体类将支持启用/禁用状态管理
  14 + /// 用于控制数据记录的可用状态
  15 + /// </remarks>
  16 + public interface IState
  17 + {
  18 + /// <summary>
  19 + /// 状态标识
  20 + /// </summary>
  21 + /// <remarks>
  22 + /// true表示启用,false表示禁用
  23 + /// </remarks>
  24 + bool State { get; set; }
  25 + }
  26 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/FileTypeEnum.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/FileTypeEnum.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Enums
  8 +{
  9 + /// <summary>
  10 + /// 文件类型枚举
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 用于定义系统支持的文件类型分类
  14 + /// 主要用于文件上传和存储时的类型区分
  15 + /// </remarks>
  16 + public enum FileTypeEnum
  17 + {
  18 + /// <summary>
  19 + /// 普通文件
  20 + /// </summary>
  21 + file = 0,
  22 +
  23 + /// <summary>
  24 + /// 图片文件
  25 + /// </summary>
  26 + image = 1,
  27 +
  28 + /// <summary>
  29 + /// 缩略图文件
  30 + /// </summary>
  31 + thumbnail = 2,
  32 +
  33 + /// <summary>
  34 + /// Excel文件
  35 + /// </summary>
  36 + excel = 3,
  37 +
  38 + /// <summary>
  39 + /// 临时文件
  40 + /// </summary>
  41 + temp = 4
  42 + }
  43 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/OrderByEnum.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/OrderByEnum.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Enums
  8 +{
  9 + /// <summary>
  10 + /// 排序方向枚举
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 用于定义数据查询时的排序方向
  14 + /// 常用于列表数据排序
  15 + /// </remarks>
  16 + public enum OrderByEnum
  17 + {
  18 + /// <summary>
  19 + /// 升序排列
  20 + /// </summary>
  21 + Asc = 0,
  22 +
  23 + /// <summary>
  24 + /// 降序排列
  25 + /// </summary>
  26 + Desc = 1
  27 + }
  28 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/QueryOperatorEnum.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/QueryOperatorEnum.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Enums
  8 +{
  9 + /// <summary>
  10 + /// 查询操作符枚举
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 定义查询条件中支持的操作符类型
  14 + /// 用于构建动态查询条件
  15 + /// </remarks>
  16 + public enum QueryOperatorEnum
  17 + {
  18 + /// <summary>
  19 + /// 等于
  20 + /// </summary>
  21 + Equal = 0,
  22 +
  23 + /// <summary>
  24 + /// 模糊匹配
  25 + /// </summary>
  26 + Like = 1,
  27 +
  28 + /// <summary>
  29 + /// 大于
  30 + /// </summary>
  31 + GreaterThan = 2,
  32 +
  33 + /// <summary>
  34 + /// 大于或等于
  35 + /// </summary>
  36 + GreaterThanOrEqual = 3,
  37 +
  38 + /// <summary>
  39 + /// 小于
  40 + /// </summary>
  41 + LessThan = 4,
  42 +
  43 + /// <summary>
  44 + /// 小于或等于
  45 + /// </summary>
  46 + LessThanOrEqual = 5,
  47 +
  48 + /// <summary>
  49 + /// 在指定集合中
  50 + /// </summary>
  51 + In = 6,
  52 +
  53 + /// <summary>
  54 + /// 不在指定集合中
  55 + /// </summary>
  56 + NotIn = 7,
  57 +
  58 + /// <summary>
  59 + /// 左侧模糊匹配
  60 + /// </summary>
  61 + LikeLeft = 8,
  62 +
  63 + /// <summary>
  64 + /// 右侧模糊匹配
  65 + /// </summary>
  66 + LikeRight = 9,
  67 +
  68 + /// <summary>
  69 + /// 不等于
  70 + /// </summary>
  71 + NoEqual = 10,
  72 +
  73 + /// <summary>
  74 + /// 为null或空
  75 + /// </summary>
  76 + IsNullOrEmpty = 11,
  77 +
  78 + /// <summary>
  79 + /// 不为null
  80 + /// </summary>
  81 + IsNot = 12,
  82 +
  83 + /// <summary>
  84 + /// 不匹配
  85 + /// </summary>
  86 + NoLike = 13,
  87 +
  88 + /// <summary>
  89 + /// 日期范围
  90 + /// </summary>
  91 + /// <remarks>
  92 + /// 使用"|"分隔起始和结束日期
  93 + /// </remarks>
  94 + DateRange = 14
  95 + }
  96 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/ResultCodeEnum.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Enums/ResultCodeEnum.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Enums
  8 +{
  9 + /// <summary>
  10 + /// API返回状态码枚举
  11 + /// </summary>
  12 + /// <remarks>
  13 + /// 定义API接口统一的返回状态码
  14 + /// 遵循HTTP状态码规范
  15 + /// </remarks>
  16 + public enum ResultCodeEnum
  17 + {
  18 + /// <summary>
  19 + /// 操作成功
  20 + /// </summary>
  21 + Success = 200,
  22 +
  23 + /// <summary>
  24 + /// 未授权访问
  25 + /// </summary>
  26 + NoPermission = 401,
  27 +
  28 + /// <summary>
  29 + /// 访问被拒绝
  30 + /// </summary>
  31 + Denied = 403,
  32 +
  33 + /// <summary>
  34 + /// 操作失败
  35 + /// </summary>
  36 + NotSuccess = 500
  37 + }
  38 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs
  1 +using System.Text;
  2 +using System.Text.RegularExpressions;
  3 +using Microsoft.AspNetCore.Http;
  4 +
  5 +namespace Yi.Framework.Core.Extensions
  6 +{
  7 + /// <summary>
  8 + /// HttpContext扩展方法类
  9 + /// </summary>
  10 + public static class HttpContextExtensions
  11 + {
  12 + /// <summary>
  13 + /// 设置内联文件下载响应头
  14 + /// </summary>
  15 + /// <param name="httpContext">HTTP上下文</param>
  16 + /// <param name="fileName">文件名</param>
  17 + public static void FileInlineHandle(this HttpContext httpContext, string fileName)
  18 + {
  19 + var encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.UTF8);
  20 + httpContext.Response.Headers.Add("Content-Disposition", $"inline;filename={encodeFilename}");
  21 + }
  22 +
  23 + /// <summary>
  24 + /// 设置附件下载响应头
  25 + /// </summary>
  26 + /// <param name="httpContext">HTTP上下文</param>
  27 + /// <param name="fileName">文件名</param>
  28 + public static void FileAttachmentHandle(this HttpContext httpContext, string fileName)
  29 + {
  30 + var encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.UTF8);
  31 + httpContext.Response.Headers.Add("Content-Disposition", $"attachment;filename={encodeFilename}");
  32 + }
  33 +
  34 + /// <summary>
  35 + /// 获取客户端首选语言
  36 + /// </summary>
  37 + /// <param name="httpContext">HTTP上下文</param>
  38 + /// <returns>语言代码,默认返回zh-CN</returns>
  39 + public static string GetLanguage(this HttpContext httpContext)
  40 + {
  41 + const string defaultLanguage = "zh-CN";
  42 + var acceptLanguage = httpContext.Request.Headers["Accept-Language"].FirstOrDefault();
  43 +
  44 + return string.IsNullOrEmpty(acceptLanguage)
  45 + ? defaultLanguage
  46 + : acceptLanguage.Split(',')[0];
  47 + }
  48 +
  49 + /// <summary>
  50 + /// 判断是否为Ajax请求
  51 + /// </summary>
  52 + /// <param name="request">HTTP请求</param>
  53 + /// <returns>是否为Ajax请求</returns>
  54 + public static bool IsAjaxRequest(this HttpRequest request)
  55 + {
  56 + const string ajaxHeader = "XMLHttpRequest";
  57 + return ajaxHeader.Equals(request.Headers["X-Requested-With"],
  58 + StringComparison.OrdinalIgnoreCase);
  59 + }
  60 +
  61 + /// <summary>
  62 + /// 获取客户端IP地址
  63 + /// </summary>
  64 + /// <param name="context">HTTP上下文</param>
  65 + /// <returns>客户端IP地址</returns>
  66 + public static string GetClientIp(this HttpContext context)
  67 + {
  68 + const string localhost = "127.0.0.1";
  69 + if (context == null) return string.Empty;
  70 +
  71 + // 尝试获取X-Forwarded-For头
  72 + var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
  73 +
  74 + // 如果没有代理头,则获取远程IP
  75 + if (string.IsNullOrEmpty(ip))
  76 + {
  77 + ip = context.Connection.RemoteIpAddress?.ToString();
  78 + }
  79 +
  80 + // 处理特殊IP
  81 + if (string.IsNullOrEmpty(ip) || ip.Contains("::1"))
  82 + {
  83 + return localhost;
  84 + }
  85 +
  86 + // 清理IPv6格式
  87 + ip = ip.Replace("::ffff:", localhost);
  88 +
  89 + // 移除端口号
  90 + ip = Regex.Replace(ip, @":\d{1,5}$", "");
  91 +
  92 + // 验证IP格式
  93 + var isValidIp = Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$") ||
  94 + Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):\d{1,5}$");
  95 +
  96 + return isValidIp ? ip : localhost;
  97 + }
  98 +
  99 + /// <summary>
  100 + /// 获取User-Agent信息
  101 + /// </summary>
  102 + /// <param name="context">HTTP上下文</param>
  103 + /// <returns>User-Agent字符串</returns>
  104 + public static string GetUserAgent(this HttpContext context)
  105 + {
  106 + return context.Request.Headers["User-Agent"].ToString();
  107 + }
  108 +
  109 + /// <summary>
  110 + /// 获取用户权限声明值
  111 + /// </summary>
  112 + /// <param name="context">HTTP上下文</param>
  113 + /// <param name="permissionsName">权限声明名称</param>
  114 + /// <returns>权限值数组</returns>
  115 + public static string[]? GetUserPermissions(this HttpContext context, string permissionsName)
  116 + {
  117 + return context.User.Claims
  118 + .Where(x => x.Type == permissionsName)
  119 + .Select(x => x.Value)
  120 + .ToArray();
  121 + }
  122 +
  123 + /// <summary>
  124 + /// 判断是否为WebSocket请求
  125 + /// </summary>
  126 + /// <param name="context">HTTP上下文</param>
  127 + /// <returns>是否为WebSocket请求</returns>
  128 + public static bool IsWebSocketRequest(this HttpContext context)
  129 + {
  130 + return context.WebSockets.IsWebSocketRequest ||
  131 + context.Request.Path == "/ws";
  132 + }
  133 + }
  134 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/AssemblyHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/AssemblyHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Reflection;
  5 +using System.Text;
  6 +using System.Threading.Tasks;
  7 +
  8 +namespace Yi.Framework.Core.Helper
  9 +{
  10 + public static class AssemblyHelper
  11 + {
  12 +
  13 + /// <summary>
  14 + /// 此处统一获取程序集,排除微软内部相关
  15 + /// </summary>
  16 + /// <returns></returns>
  17 + public static Assembly[] GetAllLoadAssembly()
  18 + {
  19 + return AppDomain.CurrentDomain.GetAssemblies();
  20 + }
  21 +
  22 + public static List<Assembly> GetReferanceAssemblies(this AppDomain domain)
  23 + {
  24 + var list = new List<Assembly>();
  25 + domain.GetAssemblies().ToList().ForEach(i =>
  26 + {
  27 + GetReferanceAssemblies(i, list);
  28 + });
  29 + return list;
  30 + }
  31 + private static void GetReferanceAssemblies(Assembly assembly, List<Assembly> list)
  32 + {
  33 + assembly.GetReferencedAssemblies().ToList().ForEach(i =>
  34 + {
  35 + var ass = Assembly.Load(i);
  36 + if (!list.Contains(ass))
  37 + {
  38 + list.Add(ass);
  39 + GetReferanceAssemblies(ass, list);
  40 + }
  41 + });
  42 + }
  43 +
  44 + public static List<Type> GetClass(string assemblyFile, string? className = null, string? spaceName = null)
  45 + {
  46 + Assembly assembly = Assembly.Load(assemblyFile);
  47 + return assembly.GetTypes().Where(m => m.IsClass
  48 + && className == null ? true : m.Name == className
  49 + && spaceName == null ? true : m.Namespace == spaceName
  50 + && !m.Name.StartsWith("<>")
  51 + ).ToList();
  52 + }
  53 +
  54 + public static List<Type> GetClassByParentClass(string assemblyFile, Type type)
  55 + {
  56 + Assembly assembly = Assembly.Load(assemblyFile);
  57 +
  58 + List<Type> resList = new List<Type>();
  59 +
  60 + List<Type> typeList = assembly.GetTypes().Where(m => m.IsClass).ToList();
  61 + foreach (var t in typeList)
  62 + {
  63 + var data = t.BaseType;
  64 + if (data == type)
  65 + {
  66 + resList.Add(t);
  67 + }
  68 +
  69 + }
  70 + return resList;
  71 + }
  72 +
  73 +
  74 + public static List<Type> GetClassByInterfaces(string assemblyFile, Type type)
  75 + {
  76 + Assembly assembly = Assembly.Load(assemblyFile);
  77 +
  78 + List<Type> resList = new List<Type>();
  79 +
  80 + List<Type> typeList = assembly.GetTypes().Where(m => m.IsClass).ToList();
  81 + foreach (var t in typeList)
  82 + {
  83 + var data = t.GetInterfaces();
  84 + if (data.Contains(type))
  85 + {
  86 + resList.Add(t);
  87 + }
  88 +
  89 + }
  90 + return resList;
  91 + }
  92 +
  93 + }
  94 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/Base32Helper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/Base32Helper.cs
  1 +using System;
  2 +using System.Text;
  3 +
  4 +namespace Yi.Framework.Core.Helper
  5 +{
  6 + public sealed class Base32Helper
  7 + {
  8 +
  9 + // the valid chars for the encoding
  10 + private static string ValidChars = "QAZ2WSX3" + "EDC4RFV5" + "TGB6YHN7" + "UJM8K9LP";
  11 +
  12 + /// <summary>
  13 + /// Converts an array of bytes to a Base32-k string.
  14 + /// </summary>
  15 + public static string ToString(byte[] bytes)
  16 + {
  17 + StringBuilder sb = new StringBuilder(); // holds the base32 chars
  18 + byte index;
  19 + int hi = 5;
  20 + int currentByte = 0;
  21 +
  22 + while (currentByte < bytes.Length)
  23 + {
  24 + // do we need to use the next byte?
  25 + if (hi > 8)
  26 + {
  27 + // get the last piece from the current byte, shift it to the right
  28 + // and increment the byte counter
  29 + index = (byte)(bytes[currentByte++] >> hi - 5);
  30 + if (currentByte != bytes.Length)
  31 + {
  32 + // if we are not at the end, get the first piece from
  33 + // the next byte, clear it and shift it to the left
  34 + index = (byte)((byte)(bytes[currentByte] << 16 - hi) >> 3 | index);
  35 + }
  36 +
  37 + hi -= 3;
  38 + }
  39 + else if (hi == 8)
  40 + {
  41 + index = (byte)(bytes[currentByte++] >> 3);
  42 + hi -= 3;
  43 + }
  44 + else
  45 + {
  46 +
  47 + // simply get the stuff from the current byte
  48 + index = (byte)((byte)(bytes[currentByte] << 8 - hi) >> 3);
  49 + hi += 5;
  50 + }
  51 +
  52 + sb.Append(ValidChars[index]);
  53 + }
  54 +
  55 + return sb.ToString();
  56 + }
  57 +
  58 +
  59 + /// <summary>
  60 + /// Converts a Base32-k string into an array of bytes.
  61 + /// </summary>
  62 + /// <exception cref="ArgumentException">
  63 + /// Input string <paramref name="s">s</paramref> contains invalid Base32-k characters.
  64 + /// </exception>
  65 + public static byte[] FromBase32String(string str)
  66 + {
  67 + int numBytes = str.Length * 5 / 8;
  68 + byte[] bytes = new byte[numBytes];
  69 +
  70 + // all UPPERCASE chars
  71 + str = str.ToUpper();
  72 +
  73 + int bit_buffer;
  74 + int currentCharIndex;
  75 + int bits_in_buffer;
  76 +
  77 + if (str.Length < 3)
  78 + {
  79 + bytes[0] = (byte)(ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5);
  80 + return bytes;
  81 + }
  82 +
  83 + bit_buffer = ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5;
  84 + bits_in_buffer = 10;
  85 + currentCharIndex = 2;
  86 + for (int i = 0; i < bytes.Length; i++)
  87 + {
  88 + bytes[i] = (byte)bit_buffer;
  89 + bit_buffer >>= 8;
  90 + bits_in_buffer -= 8;
  91 + while (bits_in_buffer < 8 && currentCharIndex < str.Length)
  92 + {
  93 + bit_buffer |= ValidChars.IndexOf(str[currentCharIndex++]) << bits_in_buffer;
  94 + bits_in_buffer += 5;
  95 + }
  96 + }
  97 +
  98 + return bytes;
  99 + }
  100 + }
  101 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ComputerHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ComputerHelper.cs
  1 +using System.Runtime.InteropServices;
  2 +using Newtonsoft.Json;
  3 +
  4 +namespace Yi.Framework.Core.Helper
  5 +{
  6 + public class ComputerHelper
  7 + {
  8 +
  9 + /// <summary>
  10 + /// 将object转换为long,若转换失败,则返回0。不抛出异常。
  11 + /// </summary>
  12 + /// <param name="str"></param>
  13 + /// <returns></returns>
  14 + private static long ParseToLong( object obj)
  15 + {
  16 + try
  17 + {
  18 + return long.Parse(obj.ToString());
  19 + }
  20 + catch
  21 + {
  22 + return 0L;
  23 + }
  24 + }
  25 +
  26 + /// <summary>
  27 + /// 将string转换为DateTime,若转换失败,则返回日期最小值。不抛出异常。
  28 + /// </summary>
  29 + /// <param name="str"></param>
  30 + /// <returns></returns>
  31 + private static DateTime ParseToDateTime( string str)
  32 + {
  33 + try
  34 + {
  35 + if (string.IsNullOrWhiteSpace(str))
  36 + {
  37 + return DateTime.MinValue;
  38 + }
  39 + if (str.Contains("-") || str.Contains("/"))
  40 + {
  41 + return DateTime.Parse(str);
  42 + }
  43 + else
  44 + {
  45 + int length = str.Length;
  46 + switch (length)
  47 + {
  48 + case 4:
  49 + return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
  50 + case 6:
  51 + return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
  52 + case 8:
  53 + return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
  54 + case 10:
  55 + return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
  56 + case 12:
  57 + return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
  58 + case 14:
  59 + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
  60 + default:
  61 + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
  62 + }
  63 + }
  64 + }
  65 + catch
  66 + {
  67 + return DateTime.MinValue;
  68 + }
  69 + }
  70 + private static double ParseToDouble(object obj)
  71 + {
  72 + try
  73 + {
  74 + return double.Parse(obj.ToString());
  75 + }
  76 + catch
  77 + {
  78 + return 0;
  79 + }
  80 + }
  81 + /// <summary>
  82 + /// CPU使用情况
  83 + /// </summary>
  84 + /// <returns></returns>
  85 + public static CPUMetrics GetCPUMetrics()
  86 + {
  87 + CPUMetrics cpuMetrics = new CPUMetrics();
  88 + var cpudetail = GetCPUDetails();
  89 + cpuMetrics.CoreTotal = cpudetail.Cores;
  90 + cpuMetrics.LogicalProcessors =cpudetail.LogicalProcessors;
  91 + cpuMetrics.CPURate = Math.Ceiling(ParseToDouble(GetCPURate()));
  92 + cpuMetrics.FreeRate = 1 - cpuMetrics.CPURate;
  93 + return cpuMetrics;
  94 + }
  95 + /// <summary>
  96 + /// 内存使用情况
  97 + /// </summary>
  98 + /// <returns></returns>
  99 + public static MemoryMetrics GetMemoryMetrics()
  100 + {
  101 + try
  102 + {
  103 + MemoryMetricsClient client = new();
  104 + MemoryMetrics memoryMetrics = IsUnix() ? client.GetUnixMetrics() : client.GetWindowsMetrics();
  105 +
  106 + memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB";
  107 + memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
  108 + memoryMetrics.TotalRAM = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
  109 + memoryMetrics.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + "%";
  110 +
  111 + return memoryMetrics;
  112 + }
  113 + catch (Exception ex)
  114 + {
  115 + Console.WriteLine("获取内存使用出错,msg=" + ex.Message + "," + ex.StackTrace);
  116 + }
  117 + return new MemoryMetrics();
  118 + }
  119 +
  120 + /// <summary>
  121 + /// 获取磁盘信息
  122 + /// </summary>
  123 + /// <returns></returns>
  124 + public static List<DiskInfo> GetDiskInfos()
  125 + {
  126 + List<DiskInfo> diskInfos = new();
  127 +
  128 + if (IsUnix())
  129 + {
  130 + try
  131 + {
  132 + string output = ShellHelper.Bash("df -m / | awk '{print $2,$3,$4,$5,$6}'");
  133 + var arr = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
  134 + if (arr.Length == 0) return diskInfos;
  135 +
  136 + var rootDisk = arr[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
  137 + if (rootDisk == null || rootDisk.Length == 0)
  138 + {
  139 + return diskInfos;
  140 + }
  141 + DiskInfo diskInfo = new()
  142 + {
  143 + DiskName = "/",
  144 + TotalSize = long.Parse(rootDisk[0]) / 1024,
  145 + Used = long.Parse(rootDisk[1]) / 1024,
  146 + AvailableFreeSpace = long.Parse(rootDisk[2]) / 1024,
  147 + AvailablePercent = decimal.Parse(rootDisk[3].Replace("%", ""))
  148 + };
  149 + diskInfos.Add(diskInfo);
  150 + }
  151 + catch (Exception ex)
  152 + {
  153 + Console.WriteLine("获取磁盘信息出错了" + ex.Message);
  154 + }
  155 + }
  156 + else
  157 + {
  158 + var driv = DriveInfo.GetDrives();
  159 + foreach (var item in driv)
  160 + {
  161 + try
  162 + {
  163 + var obj = new DiskInfo()
  164 + {
  165 + DiskName = item.Name,
  166 + TypeName = item.DriveType.ToString(),
  167 + TotalSize = item.TotalSize / 1024 / 1024 / 1024,
  168 + AvailableFreeSpace = item.AvailableFreeSpace / 1024 / 1024 / 1024,
  169 + };
  170 + obj.Used = obj.TotalSize - obj.AvailableFreeSpace;
  171 + obj.AvailablePercent = decimal.Ceiling(obj.Used / (decimal)obj.TotalSize * 100);
  172 + diskInfos.Add(obj);
  173 + }
  174 + catch (Exception ex)
  175 + {
  176 + Console.WriteLine("获取磁盘信息出错了" + ex.Message);
  177 + continue;
  178 + }
  179 + }
  180 + }
  181 +
  182 + return diskInfos;
  183 + }
  184 +
  185 + public static bool IsUnix()
  186 + {
  187 + var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
  188 + return isUnix;
  189 + }
  190 +
  191 + public static string GetCPURate()
  192 + {
  193 + string cpuRate;
  194 + if (IsUnix())
  195 + {
  196 + string output = ShellHelper.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'");
  197 + cpuRate = output.Trim();
  198 + }
  199 + else
  200 + {
  201 + string output = ShellHelper.Cmd("wmic", "cpu get LoadPercentage");
  202 + cpuRate = output.Replace("LoadPercentage", string.Empty).Trim();
  203 + }
  204 + return cpuRate;
  205 + }
  206 +
  207 + /// <summary>
  208 + /// 获取系统运行时间
  209 + /// </summary>
  210 + /// <returns></returns>
  211 + public static string GetRunTime()
  212 + {
  213 + string runTime = string.Empty;
  214 + try
  215 + {
  216 + if (IsUnix())
  217 + {
  218 + string output = ShellHelper.Bash("uptime -s").Trim();
  219 + runTime = DateTimeHelper.FormatTime(ParseToLong((DateTime.Now - ParseToDateTime(output)).TotalMilliseconds.ToString().Split('.')[0]));
  220 + }
  221 + else
  222 + {
  223 + string output = ShellHelper.Cmd("wmic", "OS get LastBootUpTime/Value");
  224 + string[] outputArr = output.Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
  225 + if (outputArr.Length == 2)
  226 + {
  227 + runTime = DateTimeHelper.FormatTime(ParseToLong((DateTime.Now - ParseToDateTime( outputArr[1].Split('.')[0])).TotalMilliseconds.ToString().Split('.')[0]));
  228 + }
  229 + }
  230 + }
  231 + catch (Exception ex)
  232 + {
  233 + Console.WriteLine("获取runTime出错" + ex.Message);
  234 + }
  235 + return runTime;
  236 + }
  237 +
  238 +
  239 +
  240 + public static CPUInfo GetCPUDetails()
  241 + {
  242 + int logicalProcessors = 0;
  243 + int cores = 0;
  244 +
  245 + if (IsUnix())
  246 + {
  247 + string logicalOutput = ShellHelper.Bash("lscpu | grep '^CPU(s):' | awk '{print $2}'");
  248 + logicalProcessors = int.Parse(logicalOutput.Trim());
  249 +
  250 + string coresOutput = ShellHelper.Bash("lscpu | grep 'Core(s) per socket:' | awk '{print $4}'");
  251 + string socketsOutput = ShellHelper.Bash("lscpu | grep 'Socket(s):' | awk '{print $2}'");
  252 + cores = int.Parse(coresOutput.Trim()) * int.Parse(socketsOutput.Trim());
  253 + }
  254 + else
  255 + {
  256 + string output = ShellHelper.Cmd("wmic", "cpu get NumberOfCores,NumberOfLogicalProcessors /format:csv");
  257 + var lines = output.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
  258 +
  259 + if (lines.Length > 1)
  260 + {
  261 + var values = lines[1].Split(',');
  262 +
  263 + cores = int.Parse(values[1].Trim());
  264 + logicalProcessors =int.Parse(values[2].Trim());
  265 + }
  266 + }
  267 +
  268 + return new CPUInfo
  269 + {
  270 + LogicalProcessors = logicalProcessors,
  271 + Cores = cores
  272 + };
  273 + }
  274 + }
  275 + public class CPUInfo
  276 + {
  277 + public int LogicalProcessors { get; set; }
  278 + public int Cores { get; set; }
  279 + }
  280 + public class CPUMetrics
  281 + {
  282 + /// <summary>
  283 + /// 内核数
  284 + /// </summary>
  285 + public int CoreTotal { get; set; }
  286 + /// <summary>
  287 + /// 逻辑处理器数
  288 + /// </summary>
  289 + public int LogicalProcessors { get; set; }
  290 + /// <summary>
  291 + /// CPU使用率%
  292 + /// </summary>
  293 + public double CPURate { get; set; }
  294 + /// <summary>
  295 + /// CPU空闲率%
  296 + /// </summary>
  297 + public double FreeRate { get; set; }
  298 + }
  299 +
  300 + /// <summary>
  301 + /// 内存信息
  302 + /// </summary>
  303 + public class MemoryMetrics
  304 + {
  305 + [JsonIgnore]
  306 + public double Total { get; set; }
  307 + [JsonIgnore]
  308 + public double Used { get; set; }
  309 + [JsonIgnore]
  310 + public double Free { get; set; }
  311 +
  312 + public string UsedRam { get; set; }
  313 +
  314 + /// <summary>
  315 + /// 总内存 GB
  316 + /// </summary>
  317 + public string TotalRAM { get; set; }
  318 + /// <summary>
  319 + /// 内存使用率 %
  320 + /// </summary>
  321 + public string RAMRate { get; set; }
  322 + /// <summary>
  323 + /// 空闲内存
  324 + /// </summary>
  325 + public string FreeRam { get; set; }
  326 + }
  327 +
  328 + public class DiskInfo
  329 + {
  330 + /// <summary>
  331 + /// 磁盘名
  332 + /// </summary>
  333 + public string DiskName { get; set; }
  334 + public string TypeName { get; set; }
  335 + public long TotalFree { get; set; }
  336 + public long TotalSize { get; set; }
  337 + /// <summary>
  338 + /// 已使用
  339 + /// </summary>
  340 + public long Used { get; set; }
  341 + /// <summary>
  342 + /// 可使用
  343 + /// </summary>
  344 + public long AvailableFreeSpace { get; set; }
  345 + public decimal AvailablePercent { get; set; }
  346 + }
  347 +
  348 + public class MemoryMetricsClient
  349 + {
  350 + #region 获取内存信息
  351 +
  352 + /// <summary>
  353 + /// windows系统获取内存信息
  354 + /// </summary>
  355 + /// <returns></returns>
  356 + public MemoryMetrics GetWindowsMetrics()
  357 + {
  358 + string output = ShellHelper.Cmd("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value");
  359 + var metrics = new MemoryMetrics();
  360 + var lines = output.Trim().Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
  361 +
  362 + if (lines.Length <= 0) return metrics;
  363 +
  364 + var freeMemoryParts = lines[0].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
  365 + var totalMemoryParts = lines[1].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
  366 +
  367 + metrics.Total = Math.Round(double.Parse(totalMemoryParts[1]) / 1024, 0);
  368 + metrics.Free = Math.Round(double.Parse(freeMemoryParts[1]) / 1024, 0);//m
  369 + metrics.Used = metrics.Total - metrics.Free;
  370 +
  371 + return metrics;
  372 + }
  373 +
  374 + /// <summary>
  375 + /// Unix系统获取
  376 + /// </summary>
  377 + /// <returns></returns>
  378 + public MemoryMetrics GetUnixMetrics()
  379 + {
  380 + string output = ShellHelper.Bash(@"
  381 +# 从 /proc/meminfo 文件中提取总内存
  382 + total_mem=$(cat /proc/meminfo | grep -i ""MemTotal"" | awk '{print $2}')
  383 + # 从 /proc/meminfo 文件中提取剩余内存
  384 +free_mem=$(cat /proc/meminfo | grep -i ""MemFree"" | awk '{print $2}')
  385 +# 显示提取的信息
  386 +echo $total_mem $used_mem $free_mem
  387 + ");
  388 + var metrics = new MemoryMetrics();
  389 +
  390 + if (!string.IsNullOrWhiteSpace(output))
  391 + {
  392 + var memory = output.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
  393 + if (memory.Length >= 2)
  394 + {
  395 + metrics.Total = Math.Round(double.Parse(memory[0]) / 1024, 0);
  396 +
  397 + metrics.Free = Math.Round(double.Parse(memory[1])/ 1024, 0);//m
  398 + metrics.Used = metrics.Total - metrics.Free;
  399 + }
  400 + }
  401 + return metrics;
  402 + }
  403 + #endregion
  404 + }
  405 +}
0 406 \ No newline at end of file
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ConsoleHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ConsoleHelper.cs
  1 +using System;
  2 +
  3 +namespace Yi.Framework.Core.Helper
  4 +{
  5 + public static class ConsoleHelper
  6 + {
  7 + public static void WriteColorLine(string str, ConsoleColor color)
  8 + {
  9 + ConsoleColor currentForeColor = Console.ForegroundColor;
  10 + Console.ForegroundColor = color;
  11 + Console.WriteLine(str);
  12 + Console.ForegroundColor = currentForeColor;
  13 + }
  14 +
  15 + /// <summary>
  16 + /// 打印错误信息
  17 + /// </summary>
  18 + /// <param name="str">待打印的字符串</param>
  19 + /// <param name="color">想要打印的颜色</param>
  20 + public static void WriteErrorLine(this string str, ConsoleColor color = ConsoleColor.Red)
  21 + {
  22 + WriteColorLine(str, color);
  23 + }
  24 +
  25 + /// <summary>
  26 + /// 打印警告信息
  27 + /// </summary>
  28 + /// <param name="str">待打印的字符串</param>
  29 + /// <param name="color">想要打印的颜色</param>
  30 + public static void WriteWarningLine(this string str, ConsoleColor color = ConsoleColor.Yellow)
  31 + {
  32 + WriteColorLine(str, color);
  33 + }
  34 + /// <summary>
  35 + /// 打印正常信息
  36 + /// </summary>
  37 + /// <param name="str">待打印的字符串</param>
  38 + /// <param name="color">想要打印的颜色</param>
  39 + public static void WriteInfoLine(this string str, ConsoleColor color = ConsoleColor.White)
  40 + {
  41 + WriteColorLine(str, color);
  42 + }
  43 + /// <summary>
  44 + /// 打印成功的信息
  45 + /// </summary>
  46 + /// <param name="str">待打印的字符串</param>
  47 + /// <param name="color">想要打印的颜色</param>
  48 + public static void WriteSuccessLine(this string str, ConsoleColor color = ConsoleColor.Green)
  49 + {
  50 + WriteColorLine(str, color);
  51 + }
  52 +
  53 + }
  54 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DateHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DateHelper.cs
  1 +using System;
  2 +
  3 +namespace Yi.Framework.Core.Helper
  4 +{
  5 + public class DateHelper
  6 + {
  7 + public static DateTime StampToDateTime(string time)
  8 + {
  9 + time = time.Substring(0, 10);
  10 + double timestamp = Convert.ToInt64(time);
  11 + DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
  12 + dateTime = dateTime.AddSeconds(timestamp).ToLocalTime();
  13 + return dateTime;
  14 + }
  15 +
  16 + public static string TimeSubTract(DateTime time1, DateTime time2)
  17 + {
  18 + TimeSpan subTract = time1.Subtract(time2);
  19 + return $"{subTract.Days} 天 {subTract.Hours} 时 {subTract.Minutes} 分 ";
  20 + }
  21 + /// <summary>
  22 + /// 时间戳转本地时间-时间戳精确到秒
  23 + /// </summary>
  24 + public static DateTime ToLocalTimeDateBySeconds(long unix)
  25 + {
  26 + var dto = DateTimeOffset.FromUnixTimeSeconds(unix);
  27 + return dto.ToLocalTime().DateTime;
  28 + }
  29 +
  30 + /// <summary>
  31 + /// 时间转时间戳Unix-时间戳精确到秒
  32 + /// </summary>
  33 + public static long ToUnixTimestampBySeconds(DateTime dt)
  34 + {
  35 + DateTimeOffset dto = new DateTimeOffset(dt);
  36 + return dto.ToUnixTimeSeconds();
  37 + }
  38 +
  39 +
  40 + /// <summary>
  41 + /// 时间戳转本地时间-时间戳精确到毫秒
  42 + /// </summary>
  43 + public static DateTime ToLocalTimeDateByMilliseconds(long unix)
  44 + {
  45 + var dto = DateTimeOffset.FromUnixTimeMilliseconds(unix);
  46 + return dto.ToLocalTime().DateTime;
  47 + }
  48 +
  49 + /// <summary>
  50 + /// 时间转时间戳Unix-时间戳精确到毫秒
  51 + /// </summary>
  52 + public static long ToUnixTimestampByMilliseconds(DateTime dt)
  53 + {
  54 + DateTimeOffset dto = new DateTimeOffset(dt);
  55 + return dto.ToUnixTimeMilliseconds();
  56 + }
  57 + }
  58 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DateTimeHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DateTimeHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Text;
  4 +
  5 +namespace Yi.Framework.Core.Helper
  6 +{
  7 + public class DateTimeHelper
  8 + {
  9 + /// <summary>
  10 + ///
  11 + /// </summary>
  12 + /// <param name="dateTime"></param>
  13 + /// <returns></returns>
  14 + public static DateTime GetBeginTime(DateTime? dateTime, int days = 0)
  15 + {
  16 + if (dateTime == DateTime.MinValue || dateTime == null)
  17 + {
  18 + return DateTime.Now.AddDays(days);
  19 + }
  20 + return dateTime ?? DateTime.Now;
  21 + }
  22 + #region 时间戳转换
  23 +
  24 + /// <summary>
  25 + /// 时间戳转本地时间-时间戳精确到秒
  26 + /// </summary>
  27 + public static DateTime ToLocalTimeDateBySeconds(long unix)
  28 + {
  29 + var dto = DateTimeOffset.FromUnixTimeSeconds(unix);
  30 + return dto.ToLocalTime().DateTime;
  31 + }
  32 +
  33 + /// <summary>
  34 + /// 时间转时间戳Unix-时间戳精确到秒
  35 + /// </summary>
  36 + public static long ToUnixTimestampBySeconds(DateTime dt)
  37 + {
  38 + DateTimeOffset dto = new DateTimeOffset(dt);
  39 + return dto.ToUnixTimeSeconds();
  40 + }
  41 +
  42 + /// <summary>
  43 + /// 时间戳转本地时间-时间戳精确到毫秒
  44 + /// </summary>
  45 + public static DateTime ToLocalTimeDateByMilliseconds(long unix)
  46 + {
  47 + var dto = DateTimeOffset.FromUnixTimeMilliseconds(unix);
  48 + return dto.ToLocalTime().DateTime;
  49 + }
  50 +
  51 + /// <summary>
  52 + /// 时间转时间戳Unix-时间戳精确到毫秒
  53 + /// </summary>
  54 + public static long ToUnixTimestampByMilliseconds(DateTime dt)
  55 + {
  56 + DateTimeOffset dto = new DateTimeOffset(dt);
  57 + return dto.ToUnixTimeMilliseconds();
  58 + }
  59 +
  60 + #endregion
  61 +
  62 + #region 毫秒转天时分秒
  63 + /// <summary>
  64 + /// 毫秒转天时分秒
  65 + /// </summary>
  66 + /// <param name="ms"></param>
  67 + /// <returns></returns>
  68 + public static string FormatTime(long ms)
  69 + {
  70 + int ss = 1000;
  71 + int mi = ss * 60;
  72 + int hh = mi * 60;
  73 + int dd = hh * 24;
  74 +
  75 + long day = ms / dd;
  76 + long hour = (ms - day * dd) / hh;
  77 + long minute = (ms - day * dd - hour * hh) / mi;
  78 + long second = (ms - day * dd - hour * hh - minute * mi) / ss;
  79 + long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss;
  80 +
  81 + string sDay = day < 10 ? "0" + day : "" + day; //天
  82 + string sHour = hour < 10 ? "0" + hour : "" + hour;//小时
  83 + string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
  84 + string sSecond = second < 10 ? "0" + second : "" + second;//秒
  85 + string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
  86 + sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
  87 +
  88 + return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond);
  89 + }
  90 + #endregion
  91 +
  92 + #region 获取unix时间戳
  93 + /// <summary>
  94 + /// 获取unix时间戳
  95 + /// </summary>
  96 + /// <param name="dt"></param>
  97 + /// <returns></returns>
  98 + public static long GetUnixTimeStamp(DateTime dt)
  99 + {
  100 + long unixTime = ((DateTimeOffset)dt).ToUnixTimeMilliseconds();
  101 + return unixTime;
  102 + }
  103 + #endregion
  104 +
  105 + #region 获取日期天的最小时间
  106 + public static DateTime GetDayMinDate(DateTime dt)
  107 + {
  108 + DateTime min = new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0);
  109 + return min;
  110 + }
  111 + #endregion
  112 +
  113 + #region 获取日期天的最大时间
  114 + public static DateTime GetDayMaxDate(DateTime dt)
  115 + {
  116 + DateTime max = new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
  117 + return max;
  118 + }
  119 + #endregion
  120 +
  121 + #region 获取日期天的最大时间
  122 + public static string FormatDateTime(DateTime? dt)
  123 + {
  124 + if (dt != null)
  125 + {
  126 + if (dt.Value.Year == DateTime.Now.Year)
  127 + {
  128 + return dt.Value.ToString("MM-dd HH:mm");
  129 + }
  130 + else
  131 + {
  132 + return dt.Value.ToString("yyyy-MM-dd HH:mm");
  133 + }
  134 + }
  135 + return string.Empty;
  136 + }
  137 + #endregion
  138 + }
  139 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DistinctHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/DistinctHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Diagnostics.CodeAnalysis;
  4 +using System.Linq;
  5 +using System.Text;
  6 +using System.Threading.Tasks;
  7 +
  8 +namespace Yi.Framework.Core.Helper
  9 +{
  10 + public class Compare<T, C> : IEqualityComparer<T>
  11 + {
  12 + private Func<T, C> _getField;
  13 + public Compare(Func<T, C> getfield)
  14 + {
  15 + _getField = getfield;
  16 + }
  17 + public bool Equals(T? x, T? y)
  18 + {
  19 + return EqualityComparer<C>.Default.Equals(_getField(x!), _getField(y!));
  20 + }
  21 +
  22 + public int GetHashCode(T obj)
  23 + {
  24 + return EqualityComparer<C>.Default.GetHashCode(_getField(obj)!);
  25 + }
  26 + }
  27 + public static class DistinctHelper
  28 + {
  29 + /// <summary>
  30 + /// 自定义Distinct扩展方法
  31 + /// </summary>
  32 + /// <typeparam name="T">要去重的对象类</typeparam>
  33 + /// <typeparam name="C">自定义去重的字段类型</typeparam>
  34 + /// <param name="source">要去重的对象</param>
  35 + /// <param name="getfield">获取自定义去重字段的委托</param>
  36 + /// <returns></returns>
  37 + public static IEnumerable<T> DistinctNew<T, C>(this IEnumerable<T> source, Func<T, C> getfield)
  38 + {
  39 + return source.Distinct(new Compare<T, C>(getfield));
  40 + }
  41 + }
  42 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/EnumHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/EnumHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Helper
  8 +{
  9 + public static class EnumHelper
  10 + {
  11 + public static New EnumToEnum<New>(this object oldEnum)
  12 + {
  13 + if (oldEnum is null)
  14 + {
  15 + throw new ArgumentNullException(nameof(oldEnum));
  16 + }
  17 + return (New)Enum.ToObject(typeof(New), oldEnum.GetHashCode());
  18 + }
  19 +
  20 + public static TEnum StringToEnum<TEnum>(this string str)
  21 + {
  22 + return (TEnum)Enum.Parse(typeof(TEnum), str);
  23 + }
  24 + }
  25 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ExpressionHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ExpressionHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Linq.Expressions;
  5 +using System.Text;
  6 +using System.Threading.Tasks;
  7 +
  8 +namespace Yi.Framework.Core.Helper
  9 +{
  10 + public static class ExpressionHelper
  11 + {
  12 +
  13 +
  14 + /// <summary>
  15 + /// Expression表达式树lambda参数拼接组合
  16 + /// </summary>
  17 + /// <typeparam name="T"></typeparam>
  18 + /// <param name="first"></param>
  19 + /// <param name="second"></param>
  20 + /// <param name="merge"></param>
  21 + /// <returns></returns>
  22 + public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
  23 + {
  24 + var parameterMap = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
  25 + var secondBody = LambdaParameteRebinder.ReplaceParameter(parameterMap, second.Body);
  26 + return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
  27 + }
  28 +
  29 + /// <summary>
  30 + /// Expression表达式树lambda参数拼接--false
  31 + /// </summary>
  32 + /// <typeparam name="T"></typeparam>
  33 + /// <returns></returns>
  34 + public static Expression<Func<T, bool>> False<T>() => f => false;
  35 +
  36 + /// <summary>
  37 + /// Expression表达式树lambda参数拼接-true
  38 + /// </summary>
  39 + /// <typeparam name="T"></typeparam>
  40 + /// <returns></returns>
  41 + public static Expression<Func<T, bool>> True<T>() => f => true;
  42 +
  43 + /// <summary>
  44 + /// Expression表达式树lambda参数拼接--and
  45 + /// </summary>
  46 + /// <typeparam name="T"></typeparam>
  47 + /// <param name="first"></param>
  48 + /// <param name="second"></param>
  49 + /// <returns></returns>
  50 + public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.Compose(second, Expression.And);
  51 +
  52 + /// <summary>
  53 + /// Expression表达式树lambda参数拼接--or
  54 + /// </summary>
  55 + /// <typeparam name="T"></typeparam>
  56 + /// <param name="first"></param>
  57 + /// <param name="second"></param>
  58 + /// <returns></returns>
  59 + public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.Compose(second, Expression.Or);
  60 + }
  61 +
  62 + public class LambdaParameteRebinder : ExpressionVisitor
  63 + {
  64 + /// <summary>
  65 + /// 存放表达式树的参数的字典
  66 + /// </summary>
  67 + private readonly Dictionary<ParameterExpression, ParameterExpression> map;
  68 +
  69 + /// <summary>
  70 + /// 构造函数
  71 + /// </summary>
  72 + /// <param name="map"></param>
  73 + public LambdaParameteRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
  74 + {
  75 + this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
  76 + }
  77 +
  78 + /// <summary>
  79 + /// 重载参数访问的方法,访问表达式树参数,如果字典中包含,则取出
  80 + /// </summary>
  81 + /// <param name="node">表达式树参数</param>
  82 + /// <returns></returns>
  83 + protected override Expression VisitParameter(ParameterExpression node)
  84 + {
  85 + if (map.TryGetValue(node, out ParameterExpression expression))
  86 + {
  87 + node = expression;
  88 + }
  89 + return base.VisitParameter(node);
  90 + }
  91 +
  92 + public static Expression ReplaceParameter(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
  93 + {
  94 + return new LambdaParameteRebinder(map).Visit(exp);
  95 + }
  96 + }
  97 +
  98 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/FileHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/FileHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.IO;
  4 +using System.Linq;
  5 +using System.Text;
  6 +
  7 +namespace Yi.Framework.Core.Helper
  8 +{
  9 + public class FileHelper : IDisposable
  10 + {
  11 +
  12 + private bool _alreadyDispose = false;
  13 +
  14 +
  15 +
  16 + #region 构造函数
  17 + public FileHelper()
  18 + {
  19 + //
  20 + // TODO: 在此处添加构造函数逻辑
  21 + //
  22 + }
  23 + ~FileHelper()
  24 + {
  25 + Dispose(); ;
  26 + }
  27 +
  28 + protected virtual void Dispose(bool isDisposing)
  29 + {
  30 + if (_alreadyDispose) return;
  31 + _alreadyDispose = true;
  32 + }
  33 + #endregion
  34 +
  35 + #region IDisposable 成员
  36 +
  37 + public void Dispose()
  38 + {
  39 + Dispose(true);
  40 + GC.SuppressFinalize(this);
  41 + }
  42 +
  43 + #endregion
  44 +
  45 + #region 取得文件后缀名
  46 + /****************************************
  47 + * 函数名称:GetPostfixStr
  48 + * 功能说明:取得文件后缀名
  49 + * 参 数:filename:文件名称
  50 + * 调用示列:
  51 + * string filename = "aaa.aspx";
  52 + * string s = EC.FileObj.GetPostfixStr(filename);
  53 + *****************************************/
  54 + /// <summary>
  55 + /// 取后缀名
  56 + /// </summary>
  57 + /// <param name="filename">文件名</param>
  58 + /// <returns>.gif|.html格式</returns>
  59 + public static string GetPostfixStr(string filename)
  60 + {
  61 + int start = filename.LastIndexOf(".");
  62 + int length = filename.Length;
  63 + string postfix = filename.Substring(start, length - start);
  64 + return postfix;
  65 + }
  66 + #endregion
  67 +
  68 + #region 根据文件大小获取指定前缀的可用文件名
  69 + /// <summary>
  70 + /// 根据文件大小获取指定前缀的可用文件名
  71 + /// </summary>
  72 + /// <param name="folderPath">文件夹</param>
  73 + /// <param name="prefix">文件前缀</param>
  74 + /// <param name="size">文件大小(1m)</param>
  75 + /// <param name="ext">文件后缀(.log)</param>
  76 + /// <returns>可用文件名</returns>
  77 + //public static string GetAvailableFileWithPrefixOrderSize(string folderPath, string prefix, int size = 1 * 1024 * 1024, string ext = ".log")
  78 + //{
  79 + // var allFiles = new DirectoryInfo(folderPath);
  80 + // var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(prefix.ToLower()) && fi.Extension.ToLower() == ext.ToLower() && fi.Length < size).OrderByDescending(d=>d.Name).ToList();
  81 +
  82 + // if (selectFiles.Count > 0)
  83 + // {
  84 + // return selectFiles.FirstOrDefault().FullName;
  85 + // }
  86 +
  87 + // return Path.Combine(folderPath, $@"{prefix}_{DateTime.Now.DateToTimeStamp()}.log");
  88 + //}
  89 + //public static string GetAvailableFileNameWithPrefixOrderSize(string _contentRoot, string prefix, int size = 1 * 1024 * 1024, string ext = ".log")
  90 + //{
  91 + // var folderPath = Path.Combine(_contentRoot, "Log");
  92 + // if (!Directory.Exists(folderPath))
  93 + // {
  94 + // Directory.CreateDirectory(folderPath);
  95 + // }
  96 +
  97 + // var allFiles = new DirectoryInfo(folderPath);
  98 + // var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(prefix.ToLower()) && fi.Extension.ToLower() == ext.ToLower() && fi.Length < size).OrderByDescending(d => d.Name).ToList();
  99 +
  100 + // if (selectFiles.Count > 0)
  101 + // {
  102 + // return selectFiles.FirstOrDefault().Name.Replace(".log","");
  103 + // }
  104 +
  105 + // return $@"{prefix}_{DateTime.Now.DateToTimeStamp()}";
  106 + //}
  107 + #endregion
  108 +
  109 + #region 写文件
  110 + /****************************************
  111 + * 函数名称:WriteFile
  112 + * 功能说明:写文件,会覆盖掉以前的内容
  113 + * 参 数:Path:文件路径,Strings:文本内容
  114 + * 调用示列:
  115 + * string Path = Server.MapPath("Default2.aspx");
  116 + * string Strings = "这是我写的内容啊";
  117 + * EC.FileObj.WriteFile(Path,Strings);
  118 + *****************************************/
  119 + /// <summary>
  120 + /// 写文件
  121 + /// </summary>
  122 + /// <param name="Path">文件路径</param>
  123 + /// <param name="Strings">文件内容</param>
  124 + public static void WriteFile(string Path, string Strings)
  125 + {
  126 + if (!File.Exists(Path))
  127 + {
  128 + FileStream f = File.Create(Path);
  129 + f.Close();
  130 + }
  131 + StreamWriter f2 = new StreamWriter(Path, false, Encoding.GetEncoding("gb2312"));
  132 + f2.Write(Strings);
  133 + f2.Close();
  134 + f2.Dispose();
  135 + }
  136 +
  137 + /// <summary>
  138 + /// 写文件
  139 + /// </summary>
  140 + /// <param name="Path">文件路径</param>
  141 + /// <param name="Strings">文件内容</param>
  142 + /// <param name="encode">编码格式</param>
  143 + public static void WriteFile(string Path, string Strings, Encoding encode)
  144 + {
  145 + if (!File.Exists(Path))
  146 + {
  147 + FileStream f = File.Create(Path);
  148 + f.Close();
  149 + }
  150 + StreamWriter f2 = new StreamWriter(Path, false, encode);
  151 + f2.Write(Strings);
  152 + f2.Close();
  153 + f2.Dispose();
  154 + }
  155 + #endregion
  156 +
  157 + #region 读文件
  158 + /****************************************
  159 + * 函数名称:ReadFile
  160 + * 功能说明:读取文本内容
  161 + * 参 数:Path:文件路径
  162 + * 调用示列:
  163 + * string Path = Server.MapPath("Default2.aspx");
  164 + * string s = EC.FileObj.ReadFile(Path);
  165 + *****************************************/
  166 + /// <summary>
  167 + /// 读文件
  168 + /// </summary>
  169 + /// <param name="Path">文件路径</param>
  170 + /// <returns></returns>
  171 + public static string ReadFile(string Path)
  172 + {
  173 + string s = "";
  174 + if (!File.Exists(Path))
  175 + s = "不存在相应的目录";
  176 + else
  177 + {
  178 + StreamReader f2 = new StreamReader(Path, Encoding.GetEncoding("gb2312"));
  179 + s = f2.ReadToEnd();
  180 + f2.Close();
  181 + f2.Dispose();
  182 + }
  183 +
  184 + return s;
  185 + }
  186 +
  187 + /// <summary>
  188 + /// 读文件
  189 + /// </summary>
  190 + /// <param name="Path">文件路径</param>
  191 + /// <param name="encode">编码格式</param>
  192 + /// <returns></returns>
  193 + public static string ReadFile(string Path, Encoding encode)
  194 + {
  195 + string s = "";
  196 + if (!File.Exists(Path))
  197 + s = "不存在相应的目录";
  198 + else
  199 + {
  200 + StreamReader f2 = new StreamReader(Path, encode);
  201 + s = f2.ReadToEnd();
  202 + f2.Close();
  203 + f2.Dispose();
  204 + }
  205 +
  206 + return s;
  207 + }
  208 + #endregion
  209 +
  210 + #region 追加文件
  211 + /****************************************
  212 + * 函数名称:FileAdd
  213 + * 功能说明:追加文件内容
  214 + * 参 数:Path:文件路径,strings:内容
  215 + * 调用示列:
  216 + * string Path = Server.MapPath("Default2.aspx");
  217 + * string Strings = "新追加内容";
  218 + * EC.FileObj.FileAdd(Path, Strings);
  219 + *****************************************/
  220 + /// <summary>
  221 + /// 追加文件
  222 + /// </summary>
  223 + /// <param name="Path">文件路径</param>
  224 + /// <param name="strings">内容</param>
  225 + public static void FileAdd(string Path, string strings)
  226 + {
  227 + StreamWriter sw = File.AppendText(Path);
  228 + sw.Write(strings);
  229 + sw.Flush();
  230 + sw.Close();
  231 + }
  232 + #endregion
  233 +
  234 + #region 拷贝文件
  235 + /****************************************
  236 + * 函数名称:FileCoppy
  237 + * 功能说明:拷贝文件
  238 + * 参 数:OrignFile:原始文件,NewFile:新文件路径
  239 + * 调用示列:
  240 + * string orignFile = Server.MapPath("Default2.aspx");
  241 + * string NewFile = Server.MapPath("Default3.aspx");
  242 + * EC.FileObj.FileCoppy(OrignFile, NewFile);
  243 + *****************************************/
  244 + /// <summary>
  245 + /// 拷贝文件
  246 + /// </summary>
  247 + /// <param name="OrignFile">原始文件</param>
  248 + /// <param name="NewFile">新文件路径</param>
  249 + public static void FileCoppy(string orignFile, string NewFile)
  250 + {
  251 + File.Copy(orignFile, NewFile, true);
  252 + }
  253 +
  254 + #endregion
  255 +
  256 + #region 删除文件
  257 + /****************************************
  258 + * 函数名称:FileDel
  259 + * 功能说明:删除文件
  260 + * 参 数:Path:文件路径
  261 + * 调用示列:
  262 + * string Path = Server.MapPath("Default3.aspx");
  263 + * EC.FileObj.FileDel(Path);
  264 + *****************************************/
  265 + /// <summary>
  266 + /// 删除文件
  267 + /// </summary>
  268 + /// <param name="Path">路径</param>
  269 + public static void FileDel(string Path)
  270 + {
  271 + File.Delete(Path);
  272 + }
  273 + #endregion
  274 +
  275 + #region 移动文件
  276 + /****************************************
  277 + * 函数名称:FileMove
  278 + * 功能说明:移动文件
  279 + * 参 数:OrignFile:原始路径,NewFile:新文件路径
  280 + * 调用示列:
  281 + * string orignFile = Server.MapPath("../说明.txt");
  282 + * string NewFile = Server.MapPath("http://www.cnblogs.com/说明.txt");
  283 + * EC.FileObj.FileMove(OrignFile, NewFile);
  284 + *****************************************/
  285 + /// <summary>
  286 + /// 移动文件
  287 + /// </summary>
  288 + /// <param name="OrignFile">原始路径</param>
  289 + /// <param name="NewFile">新路径</param>
  290 + public static void FileMove(string orignFile, string NewFile)
  291 + {
  292 + File.Move(orignFile, NewFile);
  293 + }
  294 + #endregion
  295 +
  296 + #region 在当前目录下创建目录
  297 + /****************************************
  298 + * 函数名称:FolderCreate
  299 + * 功能说明:在当前目录下创建目录
  300 + * 参 数:OrignFolder:当前目录,NewFloder:新目录
  301 + * 调用示列:
  302 + * string orignFolder = Server.MapPath("test/");
  303 + * string NewFloder = "new";
  304 + * EC.FileObj.FolderCreate(OrignFolder, NewFloder);
  305 + *****************************************/
  306 + /// <summary>
  307 + /// 在当前目录下创建目录
  308 + /// </summary>
  309 + /// <param name="OrignFolder">当前目录</param>
  310 + /// <param name="NewFloder">新目录</param>
  311 + public static void FolderCreate(string orignFolder, string NewFloder)
  312 + {
  313 + Directory.SetCurrentDirectory(orignFolder);
  314 + Directory.CreateDirectory(NewFloder);
  315 + }
  316 + #endregion
  317 +
  318 + #region 递归删除文件夹目录及文件
  319 + /****************************************
  320 + * 函数名称:DeleteFolder
  321 + * 功能说明:递归删除文件夹目录及文件
  322 + * 参 数:dir:文件夹路径
  323 + * 调用示列:
  324 + * string dir = Server.MapPath("test/");
  325 + * EC.FileObj.DeleteFolder(dir);
  326 + *****************************************/
  327 + /// <summary>
  328 + /// 递归删除文件夹目录及文件
  329 + /// </summary>
  330 + /// <param name="dir"></param>
  331 + /// <returns></returns>
  332 + public static void DeleteFolder(string dir)
  333 + {
  334 + if (Directory.Exists(dir)) //如果存在这个文件夹删除之
  335 + {
  336 + foreach (string d in Directory.GetFileSystemEntries(dir))
  337 + {
  338 + if (File.Exists(d))
  339 + File.Delete(d); //直接删除其中的文件
  340 + else
  341 + DeleteFolder(d); //递归删除子文件夹
  342 + }
  343 + Directory.Delete(dir); //删除已空文件夹
  344 + }
  345 +
  346 + }
  347 + #endregion
  348 +
  349 + #region 将指定文件夹下面的所有内容copy到目标文件夹下面 果目标文件夹为只读属性就会报错。
  350 + /****************************************
  351 + * 函数名称:CopyDir
  352 + * 功能说明:将指定文件夹下面的所有内容copy到目标文件夹下面 果目标文件夹为只读属性就会报错。
  353 + * 参 数:srcPath:原始路径,aimPath:目标文件夹
  354 + * 调用示列:
  355 + * string srcPath = Server.MapPath("test/");
  356 + * string aimPath = Server.MapPath("test1/");
  357 + * EC.FileObj.CopyDir(srcPath,aimPath);
  358 + *****************************************/
  359 + /// <summary>
  360 + /// 指定文件夹下面的所有内容copy到目标文件夹下面
  361 + /// </summary>
  362 + /// <param name="srcPath">原始路径</param>
  363 + /// <param name="aimPath">目标文件夹</param>
  364 + public static void CopyDir(string srcPath, string aimPath)
  365 + {
  366 + try
  367 + {
  368 + // 检查目标目录是否以目录分割字符结束如果不是则添加之
  369 + if (aimPath[aimPath.Length - 1] != Path.DirectorySeparatorChar)
  370 + aimPath += Path.DirectorySeparatorChar;
  371 + // 判断目标目录是否存在如果不存在则新建之
  372 + if (!Directory.Exists(aimPath))
  373 + Directory.CreateDirectory(aimPath);
  374 + // 得到源目录的文件列表,该里面是包含文件以及目录路径的一个数组
  375 + //如果你指向copy目标文件下面的文件而不包含目录请使用下面的方法
  376 + //string[] fileList = Directory.GetFiles(srcPath);
  377 + string[] fileList = Directory.GetFileSystemEntries(srcPath);
  378 + //遍历所有的文件和目录
  379 + foreach (string file in fileList)
  380 + {
  381 + //先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
  382 +
  383 + if (Directory.Exists(file))
  384 + CopyDir(file, aimPath + Path.GetFileName(file));
  385 + //否则直接Copy文件
  386 + else
  387 + File.Copy(file, aimPath + Path.GetFileName(file), true);
  388 + }
  389 +
  390 + }
  391 + catch (Exception ee)
  392 + {
  393 + throw new Exception(ee.ToString());
  394 + }
  395 + }
  396 + #endregion
  397 +
  398 + /// <summary>
  399 + /// 获取目录下全部文件名
  400 + /// </summary>
  401 + /// <param name="path"></param>
  402 + /// <param name="pattern"></param>
  403 + /// <returns></returns>
  404 + public static List<string> GetAllFileNames(string path, string pattern = "*")
  405 + {
  406 + List<FileInfo> folder = new DirectoryInfo(path).GetFiles(pattern).ToList();
  407 +
  408 + return folder.Select(x => x.Name).ToList();
  409 + }
  410 + /// <summary>
  411 + /// 文件内容替换
  412 + /// </summary>
  413 + public static string FileContentReplace(string path, string oldStr, string newStr)
  414 + {
  415 + var content = File.ReadAllText(path);
  416 +
  417 + if (content.Contains(oldStr))
  418 + {
  419 + File.Delete(path);
  420 + File.WriteAllText(path, content.Replace(oldStr, newStr));
  421 + }
  422 +
  423 + return path;
  424 + }
  425 + /// <summary>
  426 + /// 文件名称
  427 + /// </summary>
  428 + public static string FileNameReplace(string path, string oldStr, string newStr)
  429 + {
  430 + string fileName = Path.GetFileName(path);
  431 + if (!fileName.Contains(oldStr))
  432 + {
  433 + return path;
  434 + }
  435 +
  436 + string? directoryName = Path.GetDirectoryName(path);
  437 + string newFileName = fileName.Replace(oldStr, newStr);
  438 + string newPath = Path.Combine(directoryName ?? "", newFileName);
  439 + File.Move(path, newPath);
  440 +
  441 + return newPath;
  442 + }
  443 + /// <summary>
  444 + /// 目录名替换
  445 + /// </summary>
  446 + public static string DirectoryNameReplace(string path, string oldStr, string newStr)
  447 + {
  448 + string fileName = Path.GetFileName(path);
  449 + if (!fileName.Contains(oldStr))
  450 + {
  451 + return path;
  452 + }
  453 +
  454 + string? directoryName = Path.GetDirectoryName(path);
  455 + string newFileName = fileName.Replace(oldStr, newStr);
  456 + string newPath = Path.Combine(directoryName ?? "", newFileName);
  457 + Directory.Move(path, newPath);
  458 + return newPath;
  459 + }
  460 +
  461 + /// <summary>
  462 + /// 全部信息递归替换
  463 + /// </summary>
  464 + /// <param name="dirPath"></param>
  465 + /// <param name="oldStr"></param>
  466 + /// <param name="newStr"></param>
  467 + public static void AllInfoReplace(string dirPath, string oldStr, string newStr)
  468 + {
  469 + var path = DirectoryNameReplace(dirPath, oldStr, newStr);
  470 + var dirInfo = new DirectoryInfo(path);
  471 + var files = dirInfo.GetFiles();
  472 + var dirs = dirInfo.GetDirectories();
  473 + if (files.Length > 0)
  474 + {
  475 + foreach (var f in files)
  476 + {
  477 + FileContentReplace(f.FullName, oldStr, newStr);
  478 + FileNameReplace(f.FullName, oldStr, newStr);
  479 + }
  480 + }
  481 + if (dirs.Length > 0)
  482 + {
  483 + foreach (var d in dirs)
  484 + {
  485 + AllInfoReplace(d.FullName, oldStr, newStr);
  486 + }
  487 + }
  488 + }
  489 + }
  490 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/HtmlHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/HtmlHelper.cs
  1 +namespace Yi.Framework.Core.Helper
  2 +{
  3 + public static class HtmlHelper
  4 + {
  5 + #region 去除富文本中的HTML标签
  6 + /// <summary>
  7 + /// 去除富文本中的HTML标签
  8 + /// </summary>
  9 + /// <param name="html"></param>
  10 + /// <param name="length"></param>
  11 + /// <returns></returns>
  12 + public static string ReplaceHtmlTag(string html, int length = 0)
  13 + {
  14 + string strText = System.Text.RegularExpressions.Regex.Replace(html, "<[^>]+>", "");
  15 + strText = System.Text.RegularExpressions.Regex.Replace(strText, "&[^;]+;", "");
  16 +
  17 + if (length > 0 && strText.Length > length)
  18 + return strText.Substring(0, length);
  19 +
  20 + return strText;
  21 + }
  22 + #endregion
  23 + }
  24 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/HttpHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/HttpHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.IO;
  4 +using System.Linq;
  5 +using System.Net;
  6 +using System.Net.Http;
  7 +using System.Net.Mime;
  8 +using System.Text;
  9 +using System.Text.Json;
  10 +using System.Text.RegularExpressions;
  11 +using System.Threading.Tasks;
  12 +
  13 +namespace Yi.Framework.Core.Helper
  14 +{
  15 + public static class HttpHelper
  16 + {
  17 +
  18 + public static HttpClient Client { get; set; } = new HttpClient();
  19 +
  20 + public static async Task<string> Get(string url)
  21 + {
  22 + return await Client.GetStringAsync(url);
  23 + }
  24 +
  25 + public static async Task<Stream> GetIO(string url)
  26 + {
  27 + return await Client.GetStreamAsync(url);
  28 + }
  29 +
  30 +
  31 + public static async Task<string> Post(string url, object? item = null, Dictionary<string, string>? head = null)
  32 + {
  33 +
  34 + using StringContent json = new(JsonSerializer.Serialize(item), Encoding.UTF8, MediaTypeNames.Application.Json);
  35 +
  36 +
  37 + if (head is not null)
  38 + {
  39 + foreach (var d in head)
  40 + {
  41 + json.Headers.Add(d.Key, d.Value);
  42 + }
  43 + }
  44 +
  45 + var httpResponse = await Client.PostAsync(url, json);
  46 +
  47 + httpResponse.EnsureSuccessStatusCode();
  48 +
  49 + var content = httpResponse.Content;
  50 +
  51 + return await content.ReadAsStringAsync();
  52 + }
  53 +
  54 +
  55 + // public static string HttpGet(string Url, string postDataStr="")
  56 + // {
  57 + //#pragma warning disable SYSLIB0014 // 类型或成员已过时
  58 + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
  59 + //#pragma warning restore SYSLIB0014 // 类型或成员已过时
  60 + // request.Method = "GET";
  61 + // request.ContentType = "text/html;charset=UTF-8";
  62 +
  63 + // HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  64 + // Stream myResponseStream = response.GetResponseStream();
  65 + // StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
  66 + // string retString = myStreamReader.ReadToEnd();
  67 + // myStreamReader.Close();
  68 + // myResponseStream.Close();
  69 +
  70 + // return retString;
  71 + // }
  72 +
  73 + // public static bool HttpIOGet(string Url, string file, string postDataStr="")
  74 + // {
  75 + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
  76 + // request.Method = "GET";
  77 + // request.ContentType = "text/html;charset=UTF-8";
  78 +
  79 + // HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  80 + // Stream myResponseStream = response.GetResponseStream();
  81 + // FileStream writer = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
  82 + // byte[] buffer = new byte[1024];
  83 + // int c;
  84 + // while ((c = myResponseStream.Read(buffer, 0, buffer.Length)) > 0)
  85 + // {
  86 + // writer.Write(buffer, 0, c);
  87 + // }
  88 + // writer.Close();
  89 + // myResponseStream.Close();
  90 +
  91 + // return true;
  92 + // }
  93 +
  94 + // public static string HttpPost(string Url, string postDataStr="")
  95 + // {
  96 + // CookieContainer cookie = new CookieContainer();
  97 + //#pragma warning disable SYSLIB0014 // 类型或成员已过时
  98 + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
  99 + //#pragma warning restore SYSLIB0014 // 类型或成员已过时
  100 + // request.Method = "POST";
  101 + // request.ContentType = "application/x-www-form-urlencoded";
  102 + // request.ContentLength = Encoding.UTF8.GetByteCount(postDataStr);
  103 + // request.CookieContainer = cookie;
  104 +
  105 + // Stream myRequestStream = request.GetRequestStream();
  106 + // StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
  107 + // myStreamWriter.Write(postDataStr);
  108 + // myStreamWriter.Close();
  109 +
  110 + // HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  111 +
  112 + // response.Cookies = cookie.GetCookies(response.ResponseUri);
  113 + // Stream myResponseStream = response.GetResponseStream();
  114 + // StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
  115 + // string retString = myStreamReader.ReadToEnd();
  116 + // myStreamReader.Close();
  117 + // myResponseStream.Close();
  118 +
  119 + // return retString;
  120 + // }
  121 + }
  122 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/IdHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/IdHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Helper
  8 +{
  9 + public static class IdHelper
  10 + {
  11 + public static dynamic[] ToDynamicArray(this IEnumerable<long> ids)
  12 + {
  13 + return ids.Select(id => (dynamic)id).ToArray();
  14 + }
  15 + }
  16 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/IpHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/IpHelper.cs
  1 +using System.Linq;
  2 +using System.Net;
  3 +using System.Net.NetworkInformation;
  4 +using System.Net.Sockets;
  5 +
  6 +namespace Yi.Framework.Core.Helper
  7 +{
  8 + public class IpHelper
  9 + {
  10 + /// <summary>
  11 + /// 获取当前IP地址
  12 + /// </summary>
  13 + /// <param name="preferredNetworks"></param>
  14 + /// <returns></returns>
  15 + public static string GetCurrentIp(string preferredNetworks)
  16 + {
  17 + var instanceIp = "127.0.0.1";
  18 +
  19 + try
  20 + {
  21 + // 获取可用网卡
  22 + var nics = NetworkInterface.GetAllNetworkInterfaces()?.Where(network => network.OperationalStatus == OperationalStatus.Up);
  23 +
  24 + // 获取所有可用网卡IP信息
  25 + var ipCollection = nics?.Select(x => x.GetIPProperties())?.SelectMany(x => x.UnicastAddresses);
  26 +
  27 + if (ipCollection is null)
  28 + {
  29 + return instanceIp;
  30 + }
  31 +
  32 + foreach (var ipadd in ipCollection)
  33 + {
  34 + if (!IPAddress.IsLoopback(ipadd.Address) && ipadd.Address.AddressFamily == AddressFamily.InterNetwork)
  35 + {
  36 + if (string.IsNullOrEmpty(preferredNetworks))
  37 + {
  38 + instanceIp = ipadd.Address.ToString();
  39 + break;
  40 + }
  41 +
  42 + if (!ipadd.Address.ToString().StartsWith(preferredNetworks)) continue;
  43 + instanceIp = ipadd.Address.ToString();
  44 + break;
  45 + }
  46 + }
  47 + }
  48 + catch
  49 + {
  50 + // ignored
  51 + }
  52 +
  53 + return instanceIp;
  54 + }
  55 + }
  56 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/JsonHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/JsonHelper.cs
  1 +using Newtonsoft.Json;
  2 +using Newtonsoft.Json.Converters;
  3 +
  4 +namespace Yi.Framework.Core.Helper
  5 +{
  6 + public class JsonHelper
  7 + {
  8 + public static string ObjToStr<T>(T obj, string dateTimeFormat)
  9 + {
  10 + IsoDateTimeConverter timeConverter = new IsoDateTimeConverter()
  11 + {
  12 + DateTimeFormat = dateTimeFormat
  13 + };
  14 + return JsonConvert.SerializeObject(obj, Formatting.Indented, timeConverter);
  15 + }
  16 +
  17 + public static string ObjToStr<T>(T obj)
  18 + {
  19 + return JsonConvert.SerializeObject(obj);
  20 + }
  21 +
  22 + public static T StrToObj<T>(string str)
  23 + {
  24 + return JsonConvert.DeserializeObject<T>(str)!;
  25 + }
  26 + /// <summary>
  27 + /// 转换对象为JSON格式数据
  28 + /// </summary>
  29 + /// <typeparam name="T">类</typeparam>
  30 + /// <param name="obj">对象</param>
  31 + /// <returns>字符格式的JSON数据</returns>
  32 + public static string GetJSON<T>(object obj)
  33 + {
  34 + string result = string.Empty;
  35 + try
  36 + {
  37 + System.Runtime.Serialization.Json.DataContractJsonSerializer serializer =
  38 + new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
  39 + using (MemoryStream ms = new MemoryStream())
  40 + {
  41 + serializer.WriteObject(ms, obj);
  42 + result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
  43 + }
  44 + }
  45 + catch (Exception)
  46 + {
  47 + throw;
  48 + }
  49 + return result;
  50 + }
  51 + /// <summary>
  52 + /// 转换List<T>的数据为JSON格式
  53 + /// </summary>
  54 + /// <typeparam name="T">类</typeparam>
  55 + /// <param name="vals">列表值</param>
  56 + /// <returns>JSON格式数据</returns>
  57 + public string JSON<T>(List<T> vals)
  58 + {
  59 + System.Text.StringBuilder st = new System.Text.StringBuilder();
  60 + try
  61 + {
  62 + System.Runtime.Serialization.Json.DataContractJsonSerializer s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
  63 +
  64 + foreach (T city in vals)
  65 + {
  66 + using (MemoryStream ms = new MemoryStream())
  67 + {
  68 + s.WriteObject(ms, city);
  69 + st.Append(System.Text.Encoding.UTF8.GetString(ms.ToArray()));
  70 + }
  71 + }
  72 + }
  73 + catch (Exception)
  74 + {
  75 + }
  76 +
  77 + return st.ToString();
  78 + }
  79 + /// <summary>
  80 + /// JSON格式字符转换为T类型的对象
  81 + /// </summary>
  82 + /// <typeparam name="T"></typeparam>
  83 + /// <param name="jsonStr"></param>
  84 + /// <returns></returns>
  85 + public static T ParseFormByJson<T>(string jsonStr)
  86 + {
  87 + T obj = Activator.CreateInstance<T>();
  88 + using (MemoryStream ms =
  89 + new MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonStr)))
  90 + {
  91 + System.Runtime.Serialization.Json.DataContractJsonSerializer serializer =
  92 + new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
  93 + return (T)serializer.ReadObject(ms)!;
  94 + }
  95 + }
  96 +
  97 + public string JSON1<SendData>(List<SendData> vals)
  98 + {
  99 + System.Text.StringBuilder st = new System.Text.StringBuilder();
  100 + try
  101 + {
  102 + System.Runtime.Serialization.Json.DataContractJsonSerializer s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(SendData));
  103 +
  104 + foreach (SendData city in vals)
  105 + {
  106 + using (MemoryStream ms = new MemoryStream())
  107 + {
  108 + s.WriteObject(ms, city);
  109 + st.Append(System.Text.Encoding.UTF8.GetString(ms.ToArray()));
  110 + }
  111 + }
  112 + }
  113 + catch (Exception)
  114 + {
  115 + }
  116 +
  117 + return st.ToString();
  118 + }
  119 +
  120 + private static bool IsJsonStart(ref string json)
  121 + {
  122 + if (!string.IsNullOrEmpty(json))
  123 + {
  124 + json = json.Trim('\r', '\n', ' ');
  125 + if (json.Length > 1)
  126 + {
  127 + char s = json[0];
  128 + char e = json[json.Length - 1];
  129 + return s == '{' && e == '}' || s == '[' && e == ']';
  130 + }
  131 + }
  132 + return false;
  133 + }
  134 + public static bool IsJson(string json)
  135 + {
  136 + int errIndex;
  137 + return IsJson(json, out errIndex);
  138 + }
  139 + public static bool IsJson(string json, out int errIndex)
  140 + {
  141 + errIndex = 0;
  142 + if (IsJsonStart(ref json))
  143 + {
  144 + CharState cs = new CharState();
  145 + char c;
  146 + for (int i = 0; i < json.Length; i++)
  147 + {
  148 + c = json[i];
  149 + if (SetCharState(c, ref cs) && cs.childrenStart)//设置关键符号状态。
  150 + {
  151 + string item = json.Substring(i);
  152 + int err;
  153 + int length = GetValueLength(item, true, out err);
  154 + cs.childrenStart = false;
  155 + if (err > 0)
  156 + {
  157 + errIndex = i + err;
  158 + return false;
  159 + }
  160 + i = i + length - 1;
  161 + }
  162 + if (cs.isError)
  163 + {
  164 + errIndex = i;
  165 + return false;
  166 + }
  167 + }
  168 +
  169 + return !cs.arrayStart && !cs.jsonStart;
  170 + }
  171 + return false;
  172 + }
  173 +
  174 + /// <summary>
  175 + /// 获取值的长度(当Json值嵌套以"{"或"["开头时)
  176 + /// </summary>
  177 + private static int GetValueLength(string json, bool breakOnErr, out int errIndex)
  178 + {
  179 + errIndex = 0;
  180 + int len = 0;
  181 + if (!string.IsNullOrEmpty(json))
  182 + {
  183 + CharState cs = new CharState();
  184 + char c;
  185 + for (int i = 0; i < json.Length; i++)
  186 + {
  187 + c = json[i];
  188 + if (!SetCharState(c, ref cs))//设置关键符号状态。
  189 + {
  190 + if (!cs.jsonStart && !cs.arrayStart)//json结束,又不是数组,则退出。
  191 + {
  192 + break;
  193 + }
  194 + }
  195 + else if (cs.childrenStart)//正常字符,值状态下。
  196 + {
  197 + int length = GetValueLength(json.Substring(i), breakOnErr, out errIndex);//递归子值,返回一个长度。。。
  198 + cs.childrenStart = false;
  199 + cs.valueStart = 0;
  200 + //cs.state = 0;
  201 + i = i + length - 1;
  202 + }
  203 + if (breakOnErr && cs.isError)
  204 + {
  205 + errIndex = i;
  206 + return i;
  207 + }
  208 + if (!cs.jsonStart && !cs.arrayStart)//记录当前结束位置。
  209 + {
  210 + len = i + 1;//长度比索引+1
  211 + break;
  212 + }
  213 + }
  214 + }
  215 + return len;
  216 + }
  217 +
  218 + /// <summary>
  219 + /// 设置字符状态(返回true则为关键词,返回false则当为普通字符处理)
  220 + /// </summary>
  221 + private static bool SetCharState(char c, ref CharState cs)
  222 + {
  223 + cs.CheckIsError(c);
  224 + switch (c)
  225 + {
  226 + case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}]
  227 + #region 大括号
  228 + if (cs.keyStart <= 0 && cs.valueStart <= 0)
  229 + {
  230 + cs.keyStart = 0;
  231 + cs.valueStart = 0;
  232 + if (cs.jsonStart && cs.state == 1)
  233 + {
  234 + cs.childrenStart = true;
  235 + }
  236 + else
  237 + {
  238 + cs.state = 0;
  239 + }
  240 + cs.jsonStart = true;//开始。
  241 + return true;
  242 + }
  243 + #endregion
  244 + break;
  245 + case '}':
  246 + #region 大括号结束
  247 + if (cs.keyStart <= 0 && cs.valueStart < 2 && cs.jsonStart)
  248 + {
  249 + cs.jsonStart = false;//正常结束。
  250 + cs.state = 0;
  251 + cs.keyStart = 0;
  252 + cs.valueStart = 0;
  253 + cs.setDicValue = true;
  254 + return true;
  255 + }
  256 + // cs.isError = !cs.jsonStart && cs.state == 0;
  257 + #endregion
  258 + break;
  259 + case '[':
  260 + #region 中括号开始
  261 + if (!cs.jsonStart)
  262 + {
  263 + cs.arrayStart = true;
  264 + return true;
  265 + }
  266 + else if (cs.jsonStart && cs.state == 1)
  267 + {
  268 + cs.childrenStart = true;
  269 + return true;
  270 + }
  271 + #endregion
  272 + break;
  273 + case ']':
  274 + #region 中括号结束
  275 + if (cs.arrayStart && !cs.jsonStart && cs.keyStart <= 2 && cs.valueStart <= 0)//[{},333]//这样结束。
  276 + {
  277 + cs.keyStart = 0;
  278 + cs.valueStart = 0;
  279 + cs.arrayStart = false;
  280 + return true;
  281 + }
  282 + #endregion
  283 + break;
  284 + case '"':
  285 + case '\'':
  286 + #region 引号
  287 + if (cs.jsonStart || cs.arrayStart)
  288 + {
  289 + if (cs.state == 0)//key阶段,有可能是数组["aa",{}]
  290 + {
  291 + if (cs.keyStart <= 0)
  292 + {
  293 + cs.keyStart = c == '"' ? 3 : 2;
  294 + return true;
  295 + }
  296 + else if (cs.keyStart == 2 && c == '\'' || cs.keyStart == 3 && c == '"')
  297 + {
  298 + if (!cs.escapeChar)
  299 + {
  300 + cs.keyStart = -1;
  301 + return true;
  302 + }
  303 + else
  304 + {
  305 + cs.escapeChar = false;
  306 + }
  307 + }
  308 + }
  309 + else if (cs.state == 1 && cs.jsonStart)//值阶段必须是Json开始了。
  310 + {
  311 + if (cs.valueStart <= 0)
  312 + {
  313 + cs.valueStart = c == '"' ? 3 : 2;
  314 + return true;
  315 + }
  316 + else if (cs.valueStart == 2 && c == '\'' || cs.valueStart == 3 && c == '"')
  317 + {
  318 + if (!cs.escapeChar)
  319 + {
  320 + cs.valueStart = -1;
  321 + return true;
  322 + }
  323 + else
  324 + {
  325 + cs.escapeChar = false;
  326 + }
  327 + }
  328 +
  329 + }
  330 + }
  331 + #endregion
  332 + break;
  333 + case ':':
  334 + #region 冒号
  335 + if (cs.jsonStart && cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 0)
  336 + {
  337 + if (cs.keyStart == 1)
  338 + {
  339 + cs.keyStart = -1;
  340 + }
  341 + cs.state = 1;
  342 + return true;
  343 + }
  344 + // cs.isError = !cs.jsonStart || (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1);
  345 + #endregion
  346 + break;
  347 + case ',':
  348 + #region 逗号 //["aa",{aa:12,}]
  349 +
  350 + if (cs.jsonStart)
  351 + {
  352 + if (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1)
  353 + {
  354 + cs.state = 0;
  355 + cs.keyStart = 0;
  356 + cs.valueStart = 0;
  357 + //if (cs.valueStart == 1)
  358 + //{
  359 + // cs.valueStart = 0;
  360 + //}
  361 + cs.setDicValue = true;
  362 + return true;
  363 + }
  364 + }
  365 + else if (cs.arrayStart && cs.keyStart <= 2)
  366 + {
  367 + cs.keyStart = 0;
  368 + //if (cs.keyStart == 1)
  369 + //{
  370 + // cs.keyStart = -1;
  371 + //}
  372 + return true;
  373 + }
  374 + #endregion
  375 + break;
  376 + case ' ':
  377 + case '\r':
  378 + case '\n'://[ "a",\r\n{} ]
  379 + case '\0':
  380 + case '\t':
  381 + if (cs.keyStart <= 0 && cs.valueStart <= 0) //cs.jsonStart &&
  382 + {
  383 + return true;//跳过空格。
  384 + }
  385 + break;
  386 + default: //值开头。。
  387 + if (c == '\\') //转义符号
  388 + {
  389 + if (cs.escapeChar)
  390 + {
  391 + cs.escapeChar = false;
  392 + }
  393 + else
  394 + {
  395 + cs.escapeChar = true;
  396 + return true;
  397 + }
  398 + }
  399 + else
  400 + {
  401 + cs.escapeChar = false;
  402 + }
  403 + if (cs.jsonStart || cs.arrayStart) // Json 或数组开始了。
  404 + {
  405 + if (cs.keyStart <= 0 && cs.state == 0)
  406 + {
  407 + cs.keyStart = 1;//无引号的
  408 + }
  409 + else if (cs.valueStart <= 0 && cs.state == 1 && cs.jsonStart)//只有Json开始才有值。
  410 + {
  411 + cs.valueStart = 1;//无引号的
  412 + }
  413 + }
  414 + break;
  415 + }
  416 + return false;
  417 + }
  418 + }
  419 + /// <summary>
  420 + /// 字符状态
  421 + /// </summary>
  422 + public class CharState
  423 + {
  424 + internal bool jsonStart = false;//以 "{"开始了...
  425 + internal bool setDicValue = false;// 可以设置字典值了。
  426 + internal bool escapeChar = false;//以"\"转义符号开始了
  427 + /// <summary>
  428 + /// 数组开始【仅第一开头才算】,值嵌套的以【childrenStart】来标识。
  429 + /// </summary>
  430 + internal bool arrayStart = false;//以"[" 符号开始了
  431 + internal bool childrenStart = false;//子级嵌套开始了。
  432 + /// <summary>
  433 + /// 【0 初始状态,或 遇到“,”逗号】;【1 遇到“:”冒号】
  434 + /// </summary>
  435 + internal int state = 0;
  436 +
  437 + /// <summary>
  438 + /// 【-1 取值结束】【0 未开始】【1 无引号开始】【2 单引号开始】【3 双引号开始】
  439 + /// </summary>
  440 + internal int keyStart = 0;
  441 + /// <summary>
  442 + /// 【-1 取值结束】【0 未开始】【1 无引号开始】【2 单引号开始】【3 双引号开始】
  443 + /// </summary>
  444 + internal int valueStart = 0;
  445 + internal bool isError = false;//是否语法错误。
  446 +
  447 + internal void CheckIsError(char c)//只当成一级处理(因为GetLength会递归到每一个子项处理)
  448 + {
  449 + if (keyStart > 1 || valueStart > 1)
  450 + {
  451 + return;
  452 + }
  453 + //示例 ["aa",{"bbbb":123,"fff","Ddd"}]
  454 + switch (c)
  455 + {
  456 + case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}]
  457 + isError = jsonStart && state == 0;//重复开始错误 同时不是值处理。
  458 + break;
  459 + case '}':
  460 + isError = !jsonStart || keyStart != 0 && state == 0;//重复结束错误 或者 提前结束{"aa"}。正常的有{}
  461 + break;
  462 + case '[':
  463 + isError = arrayStart && state == 0;//重复开始错误
  464 + break;
  465 + case ']':
  466 + isError = !arrayStart || jsonStart;//重复开始错误 或者 Json 未结束
  467 + break;
  468 + case '"':
  469 + case '\'':
  470 + isError = !(jsonStart || arrayStart); //json 或数组开始。
  471 + if (!isError)
  472 + {
  473 + //重复开始 [""",{"" "}]
  474 + isError = state == 0 && keyStart == -1 || state == 1 && valueStart == -1;
  475 + }
  476 + if (!isError && arrayStart && !jsonStart && c == '\'')//['aa',{}]
  477 + {
  478 + isError = true;
  479 + }
  480 + break;
  481 + case ':':
  482 + isError = !jsonStart || state == 1;//重复出现。
  483 + break;
  484 + case ',':
  485 + isError = !(jsonStart || arrayStart); //json 或数组开始。
  486 + if (!isError)
  487 + {
  488 + if (jsonStart)
  489 + {
  490 + isError = state == 0 || state == 1 && valueStart > 1;//重复出现。
  491 + }
  492 + else if (arrayStart)//["aa,] [,] [{},{}]
  493 + {
  494 + isError = keyStart == 0 && !setDicValue;
  495 + }
  496 + }
  497 + break;
  498 + case ' ':
  499 + case '\r':
  500 + case '\n'://[ "a",\r\n{} ]
  501 + case '\0':
  502 + case '\t':
  503 + break;
  504 + default: //值开头。。
  505 + isError = !jsonStart && !arrayStart || state == 0 && keyStart == -1 || valueStart == -1 && state == 1;//
  506 + break;
  507 + }
  508 + //if (isError)
  509 + //{
  510 +
  511 + //}
  512 + }
  513 + }
  514 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/MD5Hepler.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/MD5Hepler.cs
  1 +using System;
  2 +using System.IO;
  3 +using System.Security.Cryptography;
  4 +using System.Text;
  5 +
  6 +namespace Yi.Framework.Core.Helper
  7 +{
  8 + public class MD5Helper
  9 + {
  10 + /// <summary>
  11 + /// 生成PasswordSalt
  12 + /// </summary>
  13 + /// <returns>返回string</returns>
  14 + public static string GenerateSalt()
  15 + {
  16 + byte[] buf = new byte[16];
  17 +#pragma warning disable SYSLIB0023 // 类型或成员已过时
  18 + new RNGCryptoServiceProvider().GetBytes(buf);
  19 +#pragma warning restore SYSLIB0023 // 类型或成员已过时
  20 + return Convert.ToBase64String(buf);
  21 + }
  22 +
  23 + /// <summary>
  24 + /// 加密密码
  25 + /// </summary>
  26 + /// <param name="pass">密码</param>
  27 + /// <param name="passwordFormat">加密类型</param>
  28 + /// <param name="salt">PasswordSalt</param>
  29 + /// <returns>加密后的密码</returns>
  30 + public static string SHA2Encode(string pass, string salt, int passwordFormat = 1)
  31 + {
  32 + if (passwordFormat == 0) // MembershipPasswordFormat.Clear
  33 + return pass;
  34 +
  35 + byte[] bIn = Encoding.Unicode.GetBytes(pass);
  36 + byte[] bSalt = Convert.FromBase64String(salt);
  37 + byte[] bAll = new byte[bSalt.Length + bIn.Length];
  38 + byte[]? bRet = null;
  39 +
  40 + Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
  41 + Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
  42 +
  43 +#pragma warning disable SYSLIB0021 // 类型或成员已过时
  44 + var s = SHA512.Create();
  45 +#pragma warning restore SYSLIB0021 // 类型或成员已过时
  46 + bRet = s.ComputeHash(bAll);
  47 +
  48 + return ConvertEx.ToUrlBase64String(bRet);
  49 + }
  50 +
  51 + /// <summary>
  52 + /// 16位MD5加密
  53 + /// </summary>
  54 + /// <param name="password"></param>
  55 + /// <returns></returns>
  56 + public static string MD5Encrypt16(string password)
  57 + {
  58 + var md5 = MD5.Create();
  59 + string t2 = BitConverter.ToString(md5.ComputeHash(Encoding.Default.GetBytes(password)), 4, 8);
  60 + t2 = t2.Replace("-", string.Empty);
  61 + return t2;
  62 + }
  63 +
  64 + /// <summary>
  65 + /// 32位MD5加密
  66 + /// </summary>
  67 + /// <param name="password"></param>
  68 + /// <returns></returns>
  69 + public static string MD5Encrypt32(string password = "")
  70 + {
  71 + string pwd = string.Empty;
  72 + try
  73 + {
  74 + if (!string.IsNullOrEmpty(password) && !string.IsNullOrWhiteSpace(password))
  75 + {
  76 + MD5 md5 = MD5.Create(); //实例化一个md5对像
  77 + // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择 
  78 + byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
  79 + // 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
  80 + foreach (var item in s)
  81 + {
  82 + // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
  83 + pwd = string.Concat(pwd, item.ToString("X2"));
  84 + }
  85 + }
  86 + }
  87 + catch
  88 + {
  89 + throw new Exception($"错误的 password 字符串:【{password}】");
  90 + }
  91 + return pwd;
  92 + }
  93 +
  94 + /// <summary>
  95 + /// 64位MD5加密
  96 + /// </summary>
  97 + /// <param name="password"></param>
  98 + /// <returns></returns>
  99 + public static string MD5Encrypt64(string password)
  100 + {
  101 + // 实例化一个md5对像
  102 + // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择 
  103 + MD5 md5 = MD5.Create();
  104 + byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
  105 + return Convert.ToBase64String(s);
  106 + }
  107 + }
  108 + public class ConvertEx
  109 + {
  110 + static readonly char[] padding = { '=' };
  111 + public static string ToUrlBase64String(byte[] inArray)
  112 + {
  113 + var str = Convert.ToBase64String(inArray);
  114 + str = str.TrimEnd(padding).Replace('+', '-').Replace('/', '_');
  115 +
  116 + return str;
  117 + }
  118 +
  119 + public static byte[] FromUrlBase64String(string s)
  120 + {
  121 + string incoming = s.Replace('_', '/').Replace('-', '+');
  122 + switch (s.Length % 4)
  123 + {
  124 + case 2: incoming += "=="; break;
  125 + case 3: incoming += "="; break;
  126 + }
  127 + byte[] bytes = Convert.FromBase64String(incoming);
  128 +
  129 + return bytes;
  130 + }
  131 + }
  132 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/MimeHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/MimeHelper.cs
  1 +using System;
  2 +using System.Collections;
  3 +using System.Collections.Generic;
  4 +using System.Linq;
  5 +using System.Text;
  6 +using System.Threading.Tasks;
  7 +using Yi.Framework.Core.Enums;
  8 +
  9 +namespace Yi.Framework.Core.Helper
  10 +{
  11 + public static class MimeHelper
  12 + {
  13 + // 通过自己定义一个静态类
  14 + // 将所有的Content Type都扔进去吧
  15 + // 调用的时候直接调用静态方法即可。
  16 +
  17 + public static List<string> ImageType { get; set; } = new List<string>
  18 + {
  19 + ".jpg",".png",".jpeg"
  20 + };
  21 +
  22 + private static Hashtable _mimeMappingTable;
  23 +
  24 + private static void AddMimeMapping(string extension, string MimeType)
  25 + {
  26 + _mimeMappingTable.Add(extension, MimeType);
  27 + }
  28 +
  29 + public static string GetMimeMapping(string FileName)
  30 + {
  31 + string text = null!;
  32 + int num = FileName.LastIndexOf('.');
  33 + if (0 < num && num > FileName.LastIndexOf('\\'))
  34 + {
  35 + text = (string)_mimeMappingTable[FileName.Substring(num)]!;
  36 + }
  37 + if (text == null)
  38 + {
  39 + text = (string)_mimeMappingTable[".*"]!;
  40 + }
  41 + return text;
  42 + }
  43 +
  44 + public static FileTypeEnum GetFileType(string fileName)
  45 + {
  46 + var extension = Path.GetExtension(fileName);
  47 + if (ImageType.Contains(extension.ToLower()))
  48 + return FileTypeEnum.image;
  49 + return FileTypeEnum.file;
  50 +
  51 +
  52 + }
  53 +
  54 + static MimeHelper()
  55 + {
  56 + _mimeMappingTable = new Hashtable(190, StringComparer.CurrentCultureIgnoreCase);
  57 + AddMimeMapping(".323", "text/h323");
  58 + AddMimeMapping(".asx", "video/x-ms-asf");
  59 + AddMimeMapping(".acx", "application/internet-property-stream");
  60 + AddMimeMapping(".ai", "application/postscript");
  61 + AddMimeMapping(".aif", "audio/x-aiff");
  62 + AddMimeMapping(".aiff", "audio/aiff");
  63 + AddMimeMapping(".axs", "application/olescript");
  64 + AddMimeMapping(".aifc", "audio/aiff");
  65 + AddMimeMapping(".asr", "video/x-ms-asf");
  66 + AddMimeMapping(".avi", "video/x-msvideo");
  67 + AddMimeMapping(".asf", "video/x-ms-asf");
  68 + AddMimeMapping(".au", "audio/basic");
  69 + AddMimeMapping(".application", "application/x-ms-application");
  70 + AddMimeMapping(".bin", "application/octet-stream");
  71 + AddMimeMapping(".bas", "text/plain");
  72 + AddMimeMapping(".bcpio", "application/x-bcpio");
  73 + AddMimeMapping(".bmp", "image/bmp");
  74 + AddMimeMapping(".cdf", "application/x-cdf");
  75 + AddMimeMapping(".cat", "application/vndms-pkiseccat");
  76 + AddMimeMapping(".crt", "application/x-x509-ca-cert");
  77 + AddMimeMapping(".c", "text/plain");
  78 + AddMimeMapping(".css", "text/css");
  79 + AddMimeMapping(".cer", "application/x-x509-ca-cert");
  80 + AddMimeMapping(".crl", "application/pkix-crl");
  81 + AddMimeMapping(".cmx", "image/x-cmx");
  82 + AddMimeMapping(".csh", "application/x-csh");
  83 + AddMimeMapping(".cod", "image/cis-cod");
  84 + AddMimeMapping(".cpio", "application/x-cpio");
  85 + AddMimeMapping(".clp", "application/x-msclip");
  86 + AddMimeMapping(".crd", "application/x-mscardfile");
  87 + AddMimeMapping(".deploy", "application/octet-stream");
  88 + AddMimeMapping(".dll", "application/x-msdownload");
  89 + AddMimeMapping(".dot", "application/msword");
  90 + AddMimeMapping(".doc", "application/msword");
  91 + AddMimeMapping(".dvi", "application/x-dvi");
  92 + AddMimeMapping(".dir", "application/x-director");
  93 + AddMimeMapping(".dxr", "application/x-director");
  94 + AddMimeMapping(".der", "application/x-x509-ca-cert");
  95 + AddMimeMapping(".dib", "image/bmp");
  96 + AddMimeMapping(".dcr", "application/x-director");
  97 + AddMimeMapping(".disco", "text/xml");
  98 + AddMimeMapping(".exe", "application/octet-stream");
  99 + AddMimeMapping(".etx", "text/x-setext");
  100 + AddMimeMapping(".evy", "application/envoy");
  101 + AddMimeMapping(".eml", "message/rfc822");
  102 + AddMimeMapping(".eps", "application/postscript");
  103 + AddMimeMapping(".flr", "x-world/x-vrml");
  104 + AddMimeMapping(".fif", "application/fractals");
  105 + AddMimeMapping(".gtar", "application/x-gtar");
  106 + AddMimeMapping(".gif", "image/gif");
  107 + AddMimeMapping(".gz", "application/x-gzip");
  108 + AddMimeMapping(".hta", "application/hta");
  109 + AddMimeMapping(".htc", "text/x-component");
  110 + AddMimeMapping(".htt", "text/webviewhtml");
  111 + AddMimeMapping(".h", "text/plain");
  112 + AddMimeMapping(".hdf", "application/x-hdf");
  113 + AddMimeMapping(".hlp", "application/winhlp");
  114 + AddMimeMapping(".html", "text/html");
  115 + AddMimeMapping(".htm", "text/html");
  116 + AddMimeMapping(".hqx", "application/mac-binhex40");
  117 + AddMimeMapping(".isp", "application/x-internet-signup");
  118 + AddMimeMapping(".iii", "application/x-iphone");
  119 + AddMimeMapping(".ief", "image/ief");
  120 + AddMimeMapping(".ivf", "video/x-ivf");
  121 + AddMimeMapping(".ins", "application/x-internet-signup");
  122 + AddMimeMapping(".ico", "image/x-icon");
  123 + AddMimeMapping(".jpg", "image/jpeg");
  124 + AddMimeMapping(".jfif", "image/pjpeg");
  125 + AddMimeMapping(".jpe", "image/jpeg");
  126 + AddMimeMapping(".jpeg", "image/jpeg");
  127 + AddMimeMapping(".js", "application/x-javascript");
  128 + AddMimeMapping(".lsx", "video/x-la-asf");
  129 + AddMimeMapping(".latex", "application/x-latex");
  130 + AddMimeMapping(".lsf", "video/x-la-asf");
  131 + AddMimeMapping(".manifest", "application/x-ms-manifest");
  132 + AddMimeMapping(".mhtml", "message/rfc822");
  133 + AddMimeMapping(".mny", "application/x-msmoney");
  134 + AddMimeMapping(".mht", "message/rfc822");
  135 + AddMimeMapping(".mid", "audio/mid");
  136 + AddMimeMapping(".mpv2", "video/mpeg");
  137 + AddMimeMapping(".man", "application/x-troff-man");
  138 + AddMimeMapping(".mvb", "application/x-msmediaview");
  139 + AddMimeMapping(".mpeg", "video/mpeg");
  140 + AddMimeMapping(".m3u", "audio/x-mpegurl");
  141 + AddMimeMapping(".mdb", "application/x-msaccess");
  142 + AddMimeMapping(".mpp", "application/vnd.ms-project");
  143 + AddMimeMapping(".m1v", "video/mpeg");
  144 + AddMimeMapping(".mpa", "video/mpeg");
  145 + AddMimeMapping(".me", "application/x-troff-me");
  146 + AddMimeMapping(".m13", "application/x-msmediaview");
  147 + AddMimeMapping(".movie", "video/x-sgi-movie");
  148 + AddMimeMapping(".m14", "application/x-msmediaview");
  149 + AddMimeMapping(".mpe", "video/mpeg");
  150 + AddMimeMapping(".mp2", "video/mpeg");
  151 + AddMimeMapping(".mov", "video/quicktime");
  152 + AddMimeMapping(".mp3", "audio/mpeg");
  153 + AddMimeMapping(".mpg", "video/mpeg");
  154 + AddMimeMapping(".ms", "application/x-troff-ms");
  155 + AddMimeMapping(".nc", "application/x-netcdf");
  156 + AddMimeMapping(".nws", "message/rfc822");
  157 + AddMimeMapping(".oda", "application/oda");
  158 + AddMimeMapping(".ods", "application/oleobject");
  159 + AddMimeMapping(".pmc", "application/x-perfmon");
  160 + AddMimeMapping(".p7r", "application/x-pkcs7-certreqresp");
  161 + AddMimeMapping(".p7b", "application/x-pkcs7-certificates");
  162 + AddMimeMapping(".p7s", "application/pkcs7-signature");
  163 + AddMimeMapping(".pmw", "application/x-perfmon");
  164 + AddMimeMapping(".ps", "application/postscript");
  165 + AddMimeMapping(".p7c", "application/pkcs7-mime");
  166 + AddMimeMapping(".pbm", "image/x-portable-bitmap");
  167 + AddMimeMapping(".ppm", "image/x-portable-pixmap");
  168 + AddMimeMapping(".pub", "application/x-mspublisher");
  169 + AddMimeMapping(".pnm", "image/x-portable-anymap");
  170 + AddMimeMapping(".png", "image/png");
  171 + AddMimeMapping(".pml", "application/x-perfmon");
  172 + AddMimeMapping(".p10", "application/pkcs10");
  173 + AddMimeMapping(".pfx", "application/x-pkcs12");
  174 + AddMimeMapping(".p12", "application/x-pkcs12");
  175 + AddMimeMapping(".pdf", "application/pdf");
  176 + AddMimeMapping(".pps", "application/vnd.ms-powerpoint");
  177 + AddMimeMapping(".p7m", "application/pkcs7-mime");
  178 + AddMimeMapping(".pko", "application/vndms-pkipko");
  179 + AddMimeMapping(".ppt", "application/vnd.ms-powerpoint");
  180 + AddMimeMapping(".pmr", "application/x-perfmon");
  181 + AddMimeMapping(".pma", "application/x-perfmon");
  182 + AddMimeMapping(".pot", "application/vnd.ms-powerpoint");
  183 + AddMimeMapping(".prf", "application/pics-rules");
  184 + AddMimeMapping(".pgm", "image/x-portable-graymap");
  185 + AddMimeMapping(".qt", "video/quicktime");
  186 + AddMimeMapping(".ra", "audio/x-pn-realaudio");
  187 + AddMimeMapping(".rgb", "image/x-rgb");
  188 + AddMimeMapping(".ram", "audio/x-pn-realaudio");
  189 + AddMimeMapping(".rmi", "audio/mid");
  190 + AddMimeMapping(".ras", "image/x-cmu-raster");
  191 + AddMimeMapping(".roff", "application/x-troff");
  192 + AddMimeMapping(".rtf", "application/rtf");
  193 + AddMimeMapping(".rtx", "text/richtext");
  194 + AddMimeMapping(".sv4crc", "application/x-sv4crc");
  195 + AddMimeMapping(".spc", "application/x-pkcs7-certificates");
  196 + AddMimeMapping(".setreg", "application/set-registration-initiation");
  197 + AddMimeMapping(".snd", "audio/basic");
  198 + AddMimeMapping(".stl", "application/vndms-pkistl");
  199 + AddMimeMapping(".setpay", "application/set-payment-initiation");
  200 + AddMimeMapping(".stm", "text/html");
  201 + AddMimeMapping(".shar", "application/x-shar");
  202 + AddMimeMapping(".sh", "application/x-sh");
  203 + AddMimeMapping(".sit", "application/x-stuffit");
  204 + AddMimeMapping(".spl", "application/futuresplash");
  205 + AddMimeMapping(".sct", "text/scriptlet");
  206 + AddMimeMapping(".scd", "application/x-msschedule");
  207 + AddMimeMapping(".sst", "application/vndms-pkicertstore");
  208 + AddMimeMapping(".src", "application/x-wais-source");
  209 + AddMimeMapping(".sv4cpio", "application/x-sv4cpio");
  210 + AddMimeMapping(".tex", "application/x-tex");
  211 + AddMimeMapping(".tgz", "application/x-compressed");
  212 + AddMimeMapping(".t", "application/x-troff");
  213 + AddMimeMapping(".tar", "application/x-tar");
  214 + AddMimeMapping(".tr", "application/x-troff");
  215 + AddMimeMapping(".tif", "image/tiff");
  216 + AddMimeMapping(".txt", "text/plain");
  217 + AddMimeMapping(".texinfo", "application/x-texinfo");
  218 + AddMimeMapping(".trm", "application/x-msterminal");
  219 + AddMimeMapping(".tiff", "image/tiff");
  220 + AddMimeMapping(".tcl", "application/x-tcl");
  221 + AddMimeMapping(".texi", "application/x-texinfo");
  222 + AddMimeMapping(".tsv", "text/tab-separated-values");
  223 + AddMimeMapping(".ustar", "application/x-ustar");
  224 + AddMimeMapping(".uls", "text/iuls");
  225 + AddMimeMapping(".vcf", "text/x-vcard");
  226 + AddMimeMapping(".wps", "application/vnd.ms-works");
  227 + AddMimeMapping(".wav", "audio/wav");
  228 + AddMimeMapping(".wrz", "x-world/x-vrml");
  229 + AddMimeMapping(".wri", "application/x-mswrite");
  230 + AddMimeMapping(".wks", "application/vnd.ms-works");
  231 + AddMimeMapping(".wmf", "application/x-msmetafile");
  232 + AddMimeMapping(".wcm", "application/vnd.ms-works");
  233 + AddMimeMapping(".wrl", "x-world/x-vrml");
  234 + AddMimeMapping(".wdb", "application/vnd.ms-works");
  235 + AddMimeMapping(".wsdl", "text/xml");
  236 + AddMimeMapping(".xap", "application/x-silverlight-app");
  237 + AddMimeMapping(".xml", "text/xml");
  238 + AddMimeMapping(".xlm", "application/vnd.ms-excel");
  239 + AddMimeMapping(".xaf", "x-world/x-vrml");
  240 + AddMimeMapping(".xla", "application/vnd.ms-excel");
  241 + AddMimeMapping(".xls", "application/vnd.ms-excel");
  242 + AddMimeMapping(".xlsx", "application/vnd.ms-excel");
  243 + AddMimeMapping(".xof", "x-world/x-vrml");
  244 + AddMimeMapping(".xlt", "application/vnd.ms-excel");
  245 + AddMimeMapping(".xlc", "application/vnd.ms-excel");
  246 + AddMimeMapping(".xsl", "text/xml");
  247 + AddMimeMapping(".xbm", "image/x-xbitmap");
  248 + AddMimeMapping(".xlw", "application/vnd.ms-excel");
  249 + AddMimeMapping(".xpm", "image/x-xpixmap");
  250 + AddMimeMapping(".xwd", "image/x-xwindowdump");
  251 + AddMimeMapping(".xsd", "text/xml");
  252 + AddMimeMapping(".z", "application/x-compress");
  253 + AddMimeMapping(".zip", "application/x-zip-compressed");
  254 + AddMimeMapping(".*", "application/octet-stream");
  255 + }
  256 + }
  257 +
  258 +
  259 +
  260 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RSAFileHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RSAFileHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.IO;
  4 +using System.Linq;
  5 +using System.Security.Cryptography;
  6 +using System.Text;
  7 +using System.Threading.Tasks;
  8 +
  9 +namespace Yi.Framework.Core.Helper
  10 +{
  11 + public class RSAFileHelper
  12 + {
  13 + public static RSA GetKey()
  14 + {
  15 + return GetRSA("key.pem");
  16 + }
  17 + public static RSA GetPublicKey()
  18 + {
  19 + return GetRSA("public.pem");
  20 + }
  21 +
  22 + private static RSA GetRSA(string fileName)
  23 + {
  24 + string rootPath = Directory.GetCurrentDirectory();
  25 + string filePath = Path.Combine(rootPath, fileName);
  26 + if (!File.Exists(filePath))
  27 + throw new Exception("文件不存在");
  28 + string key = File.ReadAllText(filePath);
  29 + var rsa = RSA.Create();
  30 + rsa.ImportFromPem(key.AsSpan());
  31 + return rsa;
  32 + }
  33 + }
  34 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RSAHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RSAHelper.cs
  1 +using System;
  2 +using System.IO;
  3 +using System.Security.Cryptography;
  4 +using System.Text;
  5 +
  6 +namespace Yi.Framework.Core.Helper
  7 +{
  8 + /// <summary>
  9 + /// RSA加解密 使用OpenSSL的公钥加密/私钥解密
  10 + /// 公私钥请使用openssl生成
  11 + /// </summary>
  12 + public class RSAHelper
  13 + {
  14 + public readonly RSA? _privateKeyRsaProvider;
  15 + public readonly RSA? _publicKeyRsaProvider;
  16 + private readonly HashAlgorithmName _hashAlgorithmName;
  17 + private readonly Encoding _encoding;
  18 +
  19 + /// <summary>
  20 + /// 实例化RSAHelper
  21 + /// </summary>
  22 + /// <param name="rsaType">加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048</param>
  23 + /// <param name="encoding">编码类型</param>
  24 + /// <param name="privateKey">私钥</param>
  25 + /// <param name="publicKey">公钥</param>
  26 + public RSAHelper(RSAType rsaType, Encoding encoding, string privateKey, string? publicKey = null)
  27 + {
  28 + _encoding = encoding;
  29 + if (!string.IsNullOrEmpty(privateKey))
  30 + {
  31 + _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);
  32 + }
  33 +
  34 + if (!string.IsNullOrEmpty(publicKey))
  35 + {
  36 + _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey);
  37 + }
  38 +
  39 + _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
  40 + }
  41 +
  42 + #region 使用私钥签名
  43 +
  44 + /// <summary>
  45 + /// 使用私钥签名
  46 + /// </summary>
  47 + /// <param name="data">原始数据</param>
  48 + /// <returns></returns>
  49 + public string Sign(string data)
  50 + {
  51 + byte[] dataBytes = _encoding.GetBytes(data);
  52 +
  53 + var signatureBytes = _privateKeyRsaProvider!.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
  54 +
  55 + return Convert.ToBase64String(signatureBytes);
  56 + }
  57 +
  58 + #endregion
  59 +
  60 + #region 使用公钥验签
  61 +
  62 + /// <summary>
  63 + /// 使用公钥验签
  64 + /// </summary>
  65 + /// <param name="data">原始数据</param>
  66 + /// <param name="sign">签名</param>
  67 + /// <returns></returns>
  68 + public bool Verify(string data, string sign)
  69 + {
  70 + byte[] dataBytes = _encoding.GetBytes(data);
  71 + byte[] signBytes = Convert.FromBase64String(sign);
  72 +
  73 + var verify = _publicKeyRsaProvider!.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
  74 +
  75 + return verify;
  76 + }
  77 +
  78 + #endregion
  79 +
  80 + #region 解密
  81 + /// <summary>
  82 + /// 私钥解密(原)
  83 + /// </summary>
  84 + /// <param name="cipherText">解密字符串(base64)</param>
  85 + /// <returns></returns>
  86 +
  87 + //public string Decrypt(string cipherText)
  88 + //{
  89 + // if (_privateKeyRsaProvider == null)
  90 + // {
  91 + // throw new Exception("_privateKeyRsaProvider is null");
  92 + // }
  93 + // return _encoding.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1));
  94 + //}
  95 + /// <summary>
  96 + /// 私钥解密(支持大量数据)
  97 + /// </summary>
  98 + /// <param name="cipherText"></param>
  99 + /// <returns></returns>
  100 + public string Decrypt(string cipherText)
  101 + {
  102 + if (_privateKeyRsaProvider == null)
  103 + {
  104 + throw new Exception("_privateKeyRsaProvider is null");
  105 + }
  106 + var bufferSize = _privateKeyRsaProvider.KeySize / 8;
  107 + byte[] buffer = new byte[bufferSize];//待解密块
  108 + using (MemoryStream msInput = new MemoryStream(Convert.FromBase64String(cipherText)))
  109 + {
  110 + using (MemoryStream msOutput = new MemoryStream())
  111 + {
  112 + int readLen; while ((readLen = msInput.Read(buffer, 0, bufferSize)) > 0)
  113 + {
  114 + byte[] dataToEnc = new byte[readLen];
  115 + Array.Copy(buffer, 0, dataToEnc, 0, readLen); byte[] encData = _privateKeyRsaProvider.Decrypt(dataToEnc, RSAEncryptionPadding.Pkcs1);
  116 + msOutput.Write(encData, 0, encData.Length);
  117 + }
  118 + byte[] result = msOutput.ToArray();
  119 + return _encoding.GetString(result);
  120 + }
  121 + }
  122 + }
  123 +
  124 + #endregion
  125 +
  126 + #region 加密
  127 +
  128 + /// <summary>
  129 + /// 公钥加密(原)
  130 + /// </summary>
  131 + /// <param name="text"></param>
  132 + /// <returns></returns>
  133 + //public string Encrypt(string text)
  134 + //{
  135 + // if (_publicKeyRsaProvider == null)
  136 + // {
  137 + // throw new Exception("_publicKeyRsaProvider is null");
  138 + // }
  139 + // return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1));
  140 + //}
  141 + /// <summary>
  142 + /// 公钥加密(支持大量数据)
  143 + /// </summary>
  144 + /// <param name="text"></param>
  145 + /// <returns></returns>
  146 + public string Encrypt(string text)
  147 + {
  148 + if (_publicKeyRsaProvider == null)
  149 + {
  150 + throw new Exception("_publicKeyRsaProvider is null");
  151 + }
  152 + var bufferSize = _publicKeyRsaProvider.KeySize / 8 - 11;
  153 + byte[] buffer = new byte[bufferSize];//待加密块
  154 +
  155 + using (MemoryStream msInput = new MemoryStream(_encoding.GetBytes(text)))
  156 + {
  157 + using (MemoryStream msOutput = new MemoryStream())
  158 + {
  159 + int readLen; while ((readLen = msInput.Read(buffer, 0, bufferSize)) > 0)
  160 + {
  161 + byte[] dataToEnc = new byte[readLen];
  162 + Array.Copy(buffer, 0, dataToEnc, 0, readLen); byte[] encData = _publicKeyRsaProvider.Encrypt(dataToEnc, RSAEncryptionPadding.Pkcs1);
  163 + msOutput.Write(encData, 0, encData.Length);
  164 + }
  165 + byte[] result = msOutput.ToArray();
  166 + return Convert.ToBase64String(result);
  167 + }
  168 + }
  169 + }
  170 +
  171 + #endregion
  172 +
  173 + #region 使用私钥创建RSA实例
  174 + /// <summary>
  175 + /// 使用私钥创建RSA实例
  176 + /// </summary>
  177 + /// <param name="privateKey"></param>
  178 + /// <returns></returns>
  179 + private RSA CreateRsaProviderFromPrivateKey(string privateKey)
  180 + {
  181 + var privateKeyBits = Convert.FromBase64String(privateKey);
  182 +
  183 + var rsa = RSA.Create();
  184 + var rsaParameters = new RSAParameters();
  185 +
  186 + using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
  187 + {
  188 + byte bt = 0;
  189 + ushort twobytes = 0;
  190 + twobytes = binr.ReadUInt16();
  191 + if (twobytes == 0x8130)
  192 + binr.ReadByte();
  193 + else if (twobytes == 0x8230)
  194 + binr.ReadInt16();
  195 + else
  196 + throw new Exception("Unexpected value read binr.ReadUInt16()");
  197 +
  198 + twobytes = binr.ReadUInt16();
  199 + if (twobytes != 0x0102)
  200 + throw new Exception("Unexpected version");
  201 +
  202 + bt = binr.ReadByte();
  203 + if (bt != 0x00)
  204 + throw new Exception("Unexpected value read binr.ReadByte()");
  205 +
  206 + rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr));
  207 + rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr));
  208 + rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr));
  209 + rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr));
  210 + rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr));
  211 + rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr));
  212 + rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr));
  213 + rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
  214 + }
  215 +
  216 + rsa.ImportParameters(rsaParameters);
  217 + return rsa;
  218 + }
  219 +
  220 + #endregion
  221 +
  222 + #region 使用公钥创建RSA实例
  223 + /// <summary>
  224 + /// 使用公钥创建RSA实例
  225 + /// </summary>
  226 + /// <param name="publicKeyString"></param>
  227 + /// <returns></returns>
  228 + public RSA? CreateRsaProviderFromPublicKey(string publicKeyString)
  229 + {
  230 + // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
  231 + byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
  232 + byte[] seq = new byte[15];
  233 +
  234 + var x509Key = Convert.FromBase64String(publicKeyString);
  235 +
  236 + // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
  237 + using (MemoryStream mem = new MemoryStream(x509Key))
  238 + {
  239 + using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading
  240 + {
  241 + byte bt = 0;
  242 + ushort twobytes = 0;
  243 +
  244 + twobytes = binr.ReadUInt16();
  245 + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  246 + binr.ReadByte(); //advance 1 byte
  247 + else if (twobytes == 0x8230)
  248 + binr.ReadInt16(); //advance 2 bytes
  249 + else
  250 + return null;
  251 +
  252 + seq = binr.ReadBytes(15); //read the Sequence OID
  253 + if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
  254 + return null;
  255 +
  256 + twobytes = binr.ReadUInt16();
  257 + if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
  258 + binr.ReadByte(); //advance 1 byte
  259 + else if (twobytes == 0x8203)
  260 + binr.ReadInt16(); //advance 2 bytes
  261 + else
  262 + return null;
  263 +
  264 + bt = binr.ReadByte();
  265 + if (bt != 0x00) //expect null byte next
  266 + return null;
  267 +
  268 + twobytes = binr.ReadUInt16();
  269 + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  270 + binr.ReadByte(); //advance 1 byte
  271 + else if (twobytes == 0x8230)
  272 + binr.ReadInt16(); //advance 2 bytes
  273 + else
  274 + return null;
  275 +
  276 + twobytes = binr.ReadUInt16();
  277 + byte lowbyte = 0x00;
  278 + byte highbyte = 0x00;
  279 +
  280 + if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
  281 + lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
  282 + else if (twobytes == 0x8202)
  283 + {
  284 + highbyte = binr.ReadByte(); //advance 2 bytes
  285 + lowbyte = binr.ReadByte();
  286 + }
  287 + else
  288 + return null;
  289 + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
  290 + int modsize = BitConverter.ToInt32(modint, 0);
  291 +
  292 + int firstbyte = binr.PeekChar();
  293 + if (firstbyte == 0x00)
  294 + { //if first byte (highest order) of modulus is zero, don't include it
  295 + binr.ReadByte(); //skip this null byte
  296 + modsize -= 1; //reduce modulus buffer size by 1
  297 + }
  298 +
  299 + byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
  300 +
  301 + if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
  302 + return null;
  303 + int expbytes = binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
  304 + byte[] exponent = binr.ReadBytes(expbytes);
  305 +
  306 + // ------- create RSACryptoServiceProvider instance and initialize with public key -----
  307 + var rsa = RSA.Create();
  308 + RSAParameters rsaKeyInfo = new RSAParameters
  309 + {
  310 + Modulus = modulus,
  311 + Exponent = exponent
  312 + };
  313 + rsa.ImportParameters(rsaKeyInfo);
  314 +
  315 + return rsa;
  316 + }
  317 +
  318 + }
  319 + }
  320 +
  321 + #endregion
  322 +
  323 + #region 导入密钥算法
  324 +
  325 + private int GetIntegerSize(BinaryReader binr)
  326 + {
  327 + byte bt = 0;
  328 + int count = 0;
  329 + bt = binr.ReadByte();
  330 + if (bt != 0x02)
  331 + return 0;
  332 + bt = binr.ReadByte();
  333 +
  334 + if (bt == 0x81)
  335 + count = binr.ReadByte();
  336 + else
  337 + if (bt == 0x82)
  338 + {
  339 + var highbyte = binr.ReadByte();
  340 + var lowbyte = binr.ReadByte();
  341 + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
  342 + count = BitConverter.ToInt32(modint, 0);
  343 + }
  344 + else
  345 + {
  346 + count = bt;
  347 + }
  348 +
  349 + while (binr.ReadByte() == 0x00)
  350 + {
  351 + count -= 1;
  352 + }
  353 + binr.BaseStream.Seek(-1, SeekOrigin.Current);
  354 + return count;
  355 + }
  356 +
  357 + private bool CompareBytearrays(byte[] a, byte[] b)
  358 + {
  359 + if (a.Length != b.Length)
  360 + return false;
  361 + int i = 0;
  362 + foreach (byte c in a)
  363 + {
  364 + if (c != b[i])
  365 + return false;
  366 + i++;
  367 + }
  368 + return true;
  369 + }
  370 +
  371 + #endregion
  372 +
  373 + }
  374 +
  375 + /// <summary>
  376 + /// RSA算法类型
  377 + /// </summary>
  378 + public enum RSAType
  379 + {
  380 + /// <summary>
  381 + /// SHA1
  382 + /// </summary>
  383 + RSA = 0,
  384 + /// <summary>
  385 + /// RSA2 密钥长度至少为2048
  386 + /// SHA256
  387 + /// </summary>
  388 + RSA2
  389 + }
  390 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RandomHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/RandomHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Text;
  4 +using System.Text.RegularExpressions;
  5 +
  6 +namespace Yi.Framework.Core.Helper
  7 +{
  8 + public class RandomHelper
  9 + {
  10 + public static string replaceBianLiang(string content)
  11 + {
  12 + content = content.Replace("{当前时间}", DateTime.Now.TimeOfDay.ToString());
  13 + string[] bianliang = new string[] { "{随机字母}", "{随机数字}", "{随机汉字}" };
  14 + Regex r;
  15 + int count;
  16 + string readstr = "";
  17 + foreach (string str in bianliang)
  18 + {
  19 + count = (content.Length - content.Replace(str, "").Length) / str.Length;
  20 + if (str == "{随机汉字}") readstr = RandChina(count);
  21 + if (str == "{随机数字}") readstr = GenerateCheckCodeNum(count);
  22 + if (str == "{随机字母}") readstr = GenerateRandomLetter(count);
  23 + if (count > readstr.Length) count = readstr.Length;
  24 + r = new Regex(str.Replace("{", "\\{").Replace("}", "\\}"));
  25 + for (int i = 0; i < count; i++)
  26 + {
  27 + content = r.Replace(content, readstr.Substring(i, 1), 1);
  28 + }
  29 + }
  30 + return content;
  31 + }
  32 +
  33 +
  34 + /// <summary>
  35 + /// 随机生成字母
  36 + /// </summary>
  37 + /// <param name="Length"></param>
  38 + /// <returns></returns>
  39 + public static string GenerateRandomLetter(int Length)
  40 + {
  41 + char[] Pattern = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
  42 + string result = "";
  43 + int n = Pattern.Length;
  44 + Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
  45 + for (int i = 0; i < Length; i++)
  46 + {
  47 + int rnd = random.Next(0, n);
  48 + result += Pattern[rnd];
  49 + }
  50 + return result;
  51 + }
  52 +
  53 + /// <summary>
  54 + /// 随机生成数字
  55 + /// </summary>
  56 + /// <param name="codeCount"></param>
  57 + /// <returns></returns>
  58 + public static string GenerateCheckCodeNum(int codeCount)
  59 + {
  60 + int rep = 0;
  61 + string str = string.Empty;
  62 + long num2 = DateTime.Now.Ticks + rep;
  63 + rep++;
  64 + Random random = new Random((int)((ulong)num2 & 0xffffffffL) | (int)(num2 >> rep));
  65 + for (int i = 0; i < codeCount; i++)
  66 + {
  67 + int num = random.Next();
  68 + str = str + ((char)(0x30 + (ushort)(num % 10))).ToString();
  69 + }
  70 + return str;
  71 + }
  72 +
  73 + /// <summary>
  74 + /// 此函数为生成指定数目的汉字
  75 + /// </summary>
  76 + /// <param name="charLen">汉字数目</param>
  77 + /// <returns>所有汉字</returns>
  78 + public static string RandChina(int charLen)
  79 + {
  80 + int area, code;//汉字由区位和码位组成(都为0-94,其中区位16-55为一级汉字区,56-87为二级汉字区,1-9为特殊字符区)
  81 + StringBuilder strtem = new StringBuilder();
  82 + Random rand = new Random();
  83 + for (int i = 0; i < charLen; i++)
  84 + {
  85 + area = rand.Next(16, 88);
  86 + if (area == 55)//第55区只有89个字符
  87 + {
  88 + code = rand.Next(1, 90);
  89 + }
  90 + else
  91 + {
  92 + code = rand.Next(1, 94);
  93 + }
  94 + strtem.Append(Encoding.GetEncoding("GB2312").GetString(new byte[] { Convert.ToByte(area + 160), Convert.ToByte(code + 160) }));
  95 + }
  96 + return strtem.ToString();
  97 + }
  98 + }
  99 +}
... ...
Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ReflexHelper.cs 0 → 100644
  1 +++ a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/ReflexHelper.cs
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using System.Threading.Tasks;
  6 +
  7 +namespace Yi.Framework.Core.Helper
  8 +{
  9 + public static class ReflexHelper
  10 + {
  11 +
  12 + #region 对象相关
  13 + /// <summary>
  14 + /// 取对象属性值
  15 + /// </summary>
  16 + /// <param name="FieldName"></param>
  17 + /// <param name="obj"></param>
  18 + /// <returns></returns>
  19 + public static string GetModelValue(string FieldName, object obj)
  20 + {
  21 + try
  22 + {
  23 + Type Ts = obj.GetType();
  24 + object o = Ts.GetProperty(FieldName).GetValue(obj, null);
  25 + if (null == o)
  26 + return null;
  27 + string Value = Convert.ToString(o);
  28 + if (string.IsNullOrEmpty(Value))
  29 + return null;
  30 + return Value;
  31 + }
  32 + catch (Exception ex)
  33 + {
  34 + throw ex;
  35 + }
  36 + return null;
  37 + }
  38 +
  39 +
  40 + /// <summary>
  41 + /// 设置对象属性值
  42 + /// </summary>
  43 + /// <param name="FieldName"></param>
  44 + /// <param name="Value"></param>
  45 + /// <param name="obj"></param>
  46 + /// <returns></returns>
  47 + public static bool SetModelValue(string FieldName, object Value, object obj)
  48 + {
  49 + try
  50 + {
  51 + Type Ts = obj.GetType();
  52 + Ts.GetProperty(FieldName).SetValue(obj, Value, null);
  53 + return true;
  54 + }
  55 + catch (Exception ex)
  56 + {
  57 + throw ex;
  58 + }
  59 + return false;
  60 + }
  61 + #endregion
  62 + }
  63 +}
... ...