Compare commits

..

5 Commits

Author SHA1 Message Date
橙子
de0c040cb3 !17 update Yi.Furion.Net6/Yi.Framework.Infrastructure/Helper/MimeHelper.cs. 后缀名拼写错误。
Merge pull request !17 from zsmygitee/N/A
2024-06-18 08:23:29 +00:00
zsmygitee
23276bb0fd update Yi.Furion.Net6/Yi.Framework.Infrastructure/Helper/MimeHelper.cs. 后缀名拼写错误。
后缀名拼写错误。

Signed-off-by: zsmygitee <274725822@qq.com>
2024-06-11 06:57:53 +00:00
陈淳
46c53e09a3 fix: 修复task注入问题 2024-01-10 15:06:15 +08:00
陈淳
209eb019d3 feat:去除多余job 2023-12-14 15:53:42 +08:00
陈淳
10a608c0af fix: 修复登录过期token的发放 2023-12-11 10:51:38 +08:00
2156 changed files with 45012 additions and 151717 deletions

135
.gitignore vendored
View File

@@ -1,7 +1,14 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
dist/
appsettings.Production.json
appsettings.Development.json
wwwroot
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
@@ -17,16 +24,21 @@
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[L]og/
[Ll]og/
# Visual Studio 2015 cache/options directory
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
@@ -40,18 +52,28 @@ TestResult.xml
[Rr]eleasePS/
dlldata.c
# DNX
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
@@ -61,6 +83,7 @@ artifacts/
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
@@ -89,6 +112,9 @@ ipch/
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
@@ -109,6 +135,14 @@ _TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
@@ -140,7 +174,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
@@ -153,12 +187,12 @@ PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
@@ -175,12 +209,13 @@ AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
!?*.[Cc]ache/
# Others
ClientBin/
@@ -188,11 +223,15 @@ ClientBin/
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
@@ -207,15 +246,20 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
@@ -225,6 +269,7 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
@@ -232,6 +277,9 @@ FakesAssemblies/
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -251,27 +299,48 @@ paket-files/
.idea/
*.sln.iml
# BookStore
src/Acme.BookStore.Web/Logs/*
src/Acme.BookStore.Web.Host/Logs/*
src/Acme.BookStore.AuthServer/Logs/*
src/Acme.BookStore.HttpApi.Host/Logs/*
src/Acme.BookStore.HttpApi.HostWithIds/Logs/*
src/Acme.BookStore.DbMigrator/Logs/*
src/Acme.BookStore.Blazor.Server/Logs/*
src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
# CodeRush personal settings
.cr/personal
# Use abp install-libs to restore.
**/wwwroot/libs/*
dist
.vscode
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Production.json
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Development.json
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Production.json
/Yi.Abp.Net8/tool/Yi.Abp.Tool.Web/appsettings.Development.json
database_backup
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Staging.json
/Yi.Abp.Net8/src/Yi.Abp.Web/logs/
/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
appsettings.Development.json

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 橙子
Copyright (c) 2022 jacktang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,213 +0,0 @@
<h1 align="center"><img align="left" height="150px" src="https://ccnetcore.com/prod-api/wwwroot/logo.png"> Yi-Framework</h1>
<h4 align="center">A .NET 8 Web open-source Asp.NetCore framework focused on user experience.</h4>
<h5 align="center">Supports Native/Abp.vNext/Furion/Ruoyi/Pure</h5>
<h2 align="center">A comprehensive solution that ultimately becomes a wheel</h2>
[![star](https://gitee.com/ccnetcore/yi/badge/star.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![fork](https://gitee.com/ccnetcore/yi/badge/fork.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![license](https://img.shields.io/badge/license-MIT-yellow)](https://gitee.com/ccnetcore/Yi)
English | [简体中文](README.md)
****
## 🍍 Introduction:
YiFramework is a DDD (Domain-Driven Design) backend open-source framework based on .Net8, Abp.vNext, and SqlSugar.
Who says ABP is complex? Who says DDD is difficult?`Breaking conventions, simplifying complexity.`,Newcomer-friendly and one of the best approaches for project extensions.
Modular design allows for the independent inclusion or exclusion of components based on business needs. It is an all-encompassing framework where you may gain unique insights.
A Comprehensive Solution, Ultimately Just Another Wheel.
(Frequent updates, feel free to watch for continuous updates.)
— This is not just a program; it is also a work of art, focused on artistic development!
> Core Features: Simple and easy to use, the framework is not referenced in a packaged form, but is provided directly with the project alongside the source code. It offers maximum freedom and complies with the MIT license, allowing for unrestricted modifications (please indicate the source).
**Branch Directory**
- Branch **Abp**: Based on the Abp.vNext branch, DDD (Domain-Driven Design) simplifies the essence of development, providing support for multiple frontends from one backend.
- Yi.Abp.Net8Backend
- Yi.Bbs.Vue3Bbs Community - Frontend
- Yi.Doc.Md: Open Source Documentation Tutorial
- Yi.Pure.Vue3Pure TS Backend Frontend
- Yi.RuoYi.Vue3RuoYi JS Backend Frontend
****
## 🍊 Official website and demo link
Let's get straight to the point and provide the link.
YiCommunity official website URL.(Bbs)[ccnetcore.com](https://ccnetcore.com) (Now live, welcome to join!)
Rbachttps://ccnetcore.com:1000 (userName cc\password 123456)
Purehttps://ccnetcore.com:1001 userNamecc\password 123456
## 🍏 Support:
- [x] Fully supports monolithic application architecture
- [x] Fully supports distributed application architecture
- [x] Fully supports microservices architecture
****
## 🍇 Explosive Detail Yi Framework Tutorial Navigation:
1. [Framework Quick Start Guide](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(Completed)
2. [Framework Functionality Module Tutorials](https://ccnetcore.com/article/8c464ab3-8ba5-2761-a4b0-3a0f83a9f312)(Completed)
3. [Practical Development Exercises](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)(Completed)
4. [Chengzi Ops CI/CD Tutorial](https://ccnetcore.com/article/6b80ed42-50cd-db15-c073-3a0fa8f7fd77)(Completed)
5. [Version Update Log](https://ccnetcore.com/article/e9e69a38-ce1e-06f5-7944-3a0fdc942ef3)(Completed)
****
## 🍓 Its philosophy:
Who says ABP is complicated? Who says DDD is difficult? Break the norm, simplify complexity, and serve as one of the best ways for newcomers and project second development.
> For every hundred people, there are a hundred different interpretations of DDD. The YiFramework may not strict adherence to DDD principles, but it is built on the shoulders of giants, distilled from numerous projects to craft a best practice.
Effortlessly achieve rapid development; typically, simplicity and elegance are hard to reconcile. The YiFramework does not solely pursue extreme decoupling but considers user experience and ease of use.
A user-oriented rapid development backend framework.
> Once you truly get hands-on, you'll understand this: extreme simplicity is also a form of elegance.
****
## 🍍 Features
- A user-oriented backend framework that is easy to use, suitable for small, medium, and enterprise-level projects.
- The project comes with the source code directly embedded, without packaging, making it ideal for secondary development and modification.
- Includes a large number of reusable modules for common scenarios.
- Elegantly supports distributed and microservices architectures.
- And more…
## 🥭 Core Technologies
#### Backend
C# Asp.NetCore 8.0
- [x] Dynamic API: Abp.vNext
- [x] Authentication and Authorization: Jwt
- [x] Logging: Serilog
- [x] Modularization: Abp.vNext
- [x] Dependency Injection: Autofac
- [x] Object Mapping: Mapster
- [x] ORM: SqlsugarCore
- [x] Multi-tenancy: Abp.vNext
- [x] Background Tasks: Quartz.Net
- [x] Local Caching: Abp.vNext
- [x] Distributed Caching: Abp.vNext
- [x] Event Bus: Abp.vNext
#### Frontend
js Vue3
- [x] Asynchronous Requests: axios
- [x] Charts: echarts
- [x] UI: element-plus
- [x] State Management: pinia
- [x] Routing: vue-router
- [x] Bundling: vite
#### DevOps
- [x] Deployment: nginx
- [x] CICD: gitlab+Jenkins
- [x] Docker: harbor
#### 🍉 Demo
<table>
<tr>
<td><img src="readme/101.png"/></td>
<td><img src="readme/102.png"/></td>
</tr>
<tr>
<td><img src="readme/103.png"/></td>
<td><img src="readme/104.png"/></td>
</tr>
</table>
<table>
<tr>
<td><img src="readme/201.png"/></td>
<td><img src="readme/202.png"/></td>
</tr>
<tr>
<td><img src="readme/203.png"/></td>
<td><img src="readme/204.png"/></td>
</tr>
<tr>
<td><img src="readme/205.png"/></td>
<td><img src="readme/206.png"/></td>
</tr>
</table>
<table>
<tr>
<td><img src="readme/1.png"/></td>
<td><img src="readme/2.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/5.png"/></td>
<td><img src="readme/6.png"/></td>
</tr>
<tr>
<td><img src="readme/7.png"/></td>
<td><img src="readme/8.png"/></td>
</tr>
<tr>
<td><img src="readme/9.png"/></td>
<td><img src="readme/10.png"/></td>
</tr>
<tr>
<td><img src="readme/11.png"/></td>
<td><img src="readme/12.png"/></td>
</tr>
</table>
## 🌶 Thank you
[橙子]https://ccnetcore.com
[XWen]https://gitee.com/on-wensil
[朝夕教育]https://www.zhaoxiedu.net
[Sqlsugar老杰哥]https://www.donet5.com/Home/Doc
[车神]微信公众号搜索Dotnet技术进阶
[RuYiAdmin如意老兄]https://gitee.com/pang-mingjun/RuYiAdmin
[ZrAdminNetCore字母老哥]https://gitee.com/izory/ZrAdminNetCore
[Admin.NET]https://gitee.com/zuohuaijun/Admin.NET
[Furion百小僧]https://furion.baiqian.ltd/
****
## 🌽 Contact Us:
Author's QQ`454313500`
QQ group chat官方一群Full、官方二群Full、官方三群`786308927`Full、官方四群:`498310311`Full、官方五群:`981136525`New
WeChat Group Chat官方微信一群Full、官方微信二群
WeChat Community: Add the author's WeChat chengzilaoge520 橙子老哥520,Note: Join the group.
Contact the author, everyone here is a consultant.
Official website message area[ccnetcore.com](https://ccnetcore.com)
****
## 🍄 FQA:
Visit the official website to view the message board.
[the message board](https://ccnetcore.com/discuss/1641030787056930818)

312
README.md
View File

@@ -1,127 +1,184 @@
<h1 align="center"><img align="left" height="150px" src="https://ccnetcore.com/prod-api/wwwroot/logo.png"> Yi框架</h1>
<h4 align="center">一套以用户体验出发的.Net8 Web开源框架</h4>
<h5 align="center">支持Abp.vNext 版本原生版本、Furion版本前端接入Ruoyi/Pure Vue</h5>
<h1 align="center"><img align="left" height="150px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1>
<h4 align="center">一套以用户体验出发的.Net6 Web开源框架</h4>
<h5 align="center">支持原生版本、Furion版本、Abp版本,前端后台接入Ruoyi Vue3.0</h5>
<h2 align="center">集大成者,终究轮子</h2>
[![star](https://gitee.com/ccnetcore/yi/badge/star.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![fork](https://gitee.com/ccnetcore/yi/badge/fork.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![license](https://img.shields.io/badge/license-MIT-yellow)](https://gitee.com/ccnetcore/Yi)
[English](README-en.md) | 简体中文
![sdk](https://img.shields.io/badge/sdk-6.0.1-d.svg)![License MIT](https://img.shields.io/badge/license-Apache-blue.svg?style=flat-square)
****
## 🍍 简介:
YiFramework是一个基于.Net8+Abp.vNext+SqlSugar的DDD领域驱动设计后端开源框架
谁说Abp复杂谁说DDD难`打破常规,化繁为简`,新人入门,项目二开,最佳方式之一
### 简介:
**中文:意框架**和他的名字一样“简易”同时接入Java的Ruoyi Vue3.0前端)
模块,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解
模块分化较多,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解
**英文YiFramework**
Yi框架-一套与SqlSugar一样爽的.Net8开源框架。
Yi框架-一套与SqlSugar一样爽的.Net6开源框架。
与Sqlsugar理念一致以用户体验出发。
适合.Net8学习、Sqlsugar学习 、项目二次开发。
适合.Net6学习、Sqlsugar学习 、项目二次开发。
集大成者,终究轮子
更新频繁可watching持续关注。
Yi框架最新版本标签`v3.0.0`,具体版本可以查看标签迭代
项目与Sqlsugar同步更新但这作者老杰哥代码天天爆肝到凌晨两点我们也尽量会跟上他的脚步。更新频繁所以可watching持续关注。
————这不仅仅是一个程序,更是一个艺术品,面向艺术的开发!
> 核心特点简单好用框架不以打包形式引用而是直接以项目附带源码给出自由度拉满遵循Mit协议允许随意修改请注明来源即可
**分支目录**
**分支:**
- 分支**Abp**: 基于Abp.vNext分支DDD领域驱动设计,回归开发本质,极度简单,一个后台支持以下多个前端
本项目由EFCore版本历经4年不断迭代至Sqlsugar版本现EFcore版本已弃用目前sqlsugar已带业务功能
- Yi.Abp.Net8后端
- Yi.Bbs.Vue3Bbs社区 前端
- Yi.Doc.Md: 开源文档教程
- Yi.Pure.Vue3Pure ts后台前端
- Yi.RuoYi.Vue3RuoYi js后台前端
- (推荐) **Furion**: 基于Furion分支回归开发本质极度简单用起来贼爽
- ~~**Framework**~~: 框架分支,所有东西都在这里
- ~~**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构~~
- ~~**SqlSugar-Dev**为sqlsugar分支的实时开发版本~~
- ~~**abp**:基于abp.vnext项目~~
****
## 🍊 官网及演示地址:
**目录:**
废话少说直接上地址
Yi后端框架分为3个部分
Yi社区官网网址Bbs社区正式[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
- Infrastructure基础设施框架底层+sqlsugar+furion
- Module应用模块可选项例如缓存模块、微信模块、文件模块、日志模块等
- Application业务模块用于开发
Rbac后台演示地址https://ccnetcore.com:1000 用户cc、密码123456
另外光说不练假把式我们不仅仅提供一个空白的框架还同时提供3个基于yi框架的业务模块没有听错目前为1个后端支持3个前端。各个模块关系解耦可单独使用其中的任意业务模块
Pure后台演示地址https://ccnetcore.com:1001 用户cc、密码123456
- Yi.RuoYi.Vue3Ruoyi后台管理系统Rbac Vue3前端推荐
## 🍏 支持:
- Yi.Furion.Net6.NET6后端推荐
- Yi.App.Vue3移动端App Vue3前端
- Yi.BBS.Vue3Web网页端BBS论坛 Vue3+Ts前端
后续我们持续更新各大应用模块及业务模块shop商场、erp进销存、mes工厂系统等
业务支持并扩展至各个领域,用于具体项目的二次开发极大复用后端代码及前端代码,以通用的部分+不通的部分快速二开
### 演示地址:
废话少说直接上地址,**请不要**更改里面的数据
官网网址:[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
Bbs社区系统[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
Rbac后台管理系统[yi.ccnetcore.com](http://yi.ccnetcore.com) (已上线)~~管理员账号cc 、 123456~~
App移动端系统[xxx](xxx)正在部署
网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~(目前使用单体架构部署,无需网关)
### 支持:
- [x] 完全支持单体应用架构
- [x] 完全支持分布式应用架构
- [x] 完全支持微服务架构
****
## 🍇 详细到爆炸的Yi框架教程导航
### 详细到爆炸的Yi框架教程导航
1. [框架快速开始教程](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(已完成)
2. [框架功能模块教程](https://ccnetcore.com/article/8c464ab3-8ba5-2761-a4b0-3a0f83a9f312)(已完成)
3. [实战演练开发教程](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)(已完成)
4. [橙子运维CICD教程](https://ccnetcore.com/article/6b80ed42-50cd-db15-c073-3a0fa8f7fd77)(已完成)
5. [版本更新日志](https://ccnetcore.com/article/e9e69a38-ce1e-06f5-7944-3a0fdc942ef3)(已完成)
1. [框架快速开始](https://ccnetcore.com/article/1641733850189139969)(已完成)
2. [框架模块教程](https://ccnetcore.com/article/1641733991574933505)(已完成)
3. [应用模块教程](https://ccnetcore.com/article/1641734073091231745)
4. [Yi.RBAC后台系统](https://ccnetcore.com/article/1641734171128893441)
5. [Yi.BBS社区系统](https://ccnetcore.com/article/1641734308475572225)
****
## 🍓 它的理念:
谁说Abp复杂谁说DDD难打破常规化繁为简新人入门项目二开最佳方式之一
> 一百个人就有一百种DDDYi框架不一定是极度严格的DDD而是站在巨人的肩膀上经过极多项目的提炼摸索出一种最佳实践
### 它的理念:
优雅的进行快速开发通常简单程度与优雅程度不可兼得Yi框架并不一昧的追求极致的解耦会站在用户使用角度上在使用难易度进行考虑衡量
例如我们大部分功能紧密贴合Sqlsugar虽然缺少其他orm的替换性但在使用程度上降低的使用难度
> 一个面向用户的快速开发后端框架
在真正的使用,你会明白这一点,极致的简单,也是优雅的一种体现。
在真正的使用,你会明白这一点,极致的简单,也是优雅的一种体现。
****
## 🍍 特点
- 面向用户的后端框架,使用简单,适合小型、中型、企业级项目
- 项目直接内置源码,不打包,非常适合进行二开改造
### 特点:
- 面向用户的后端框架,使用简单,适合小型、企业级项目
- 项目内置源码,不打包
- 开箱即用
- 支持模块化
- 支持动态Api
- 支持属性注入
- 内置包含大量通用场景模块
- 优雅支持分布式及微服务架构
- 等等
## 🍍 基础设施简介
### 基础设施简介
- Jwt鉴权
- 接口级别授权
- 对象映射
- O/RM
- 数据过滤
- 多租户
- 逻辑删除
- 审计日志
- 种子数据
- 工作单元
- 模块化
- 动态Api
- 属性注入
- 自动依赖注入
- 当前用户
- 仓储
- Crud
以下全部功能可直接使用:
- [Abp.vNext官网](https://docs.abp.io/zh-Hans/abp/latest/)
### 内置模块简介
- 后台任务
- 本地缓存
- 分布式缓存
- 事件总线
- 字典管理
- 文件管理
- 图片操作
- Excel操作
- 操作日志管理
- Sms短信
- 微信支付
- WebFirst代码生成
- [SqlSugar官网](https://www.donet5.com/home/doc)
## 🍅 内置模块简介
- Rbac权限管理系统已上线支持pure、ruoyi前端
- Bbs论坛社区系统已上线
### 业务项目
- RABC后台管理系统
- BBS社区系统
- APP移动端系统
> 重复的东西,无需再写一遍,这也是优雅的体现之一
****
## 🥭 核心技术
### 核心技术
#### 后端
C# Asp.NetCore 8.0
- [x] 动态ApiAbp.vNext
C# Asp.NetCore 6.0
- [x] 动态ApiCike.AutoApi
- [x] 鉴权授权Jwt
- [x] 日志:Serilog
- [x] 模块化:Abp.vNext
- [x] 日志:Nlog
- [x] 模块化:StartupModules
- [x] 依赖注入Autofac
- [x] 对象映射Mapster
- [x] ORM: SqlsugarCore
- [x] 多租户Abp.vNext
- [x] ORM:SqlsugarCore
- [x] 多租户Abp
- [x] 后台任务Quartz.Net
- [x] 本地缓存:Abp.vNext
- [x] 分布式缓存:Abp.vNext
- [x] 事件总线:Abp.vNext
- [x] 本地缓存:MemortCache
- [x] 分布式缓存:CSRedisCore
- [x] 事件总线:Cike.EventBus
- [x] 图像操作SixLabors.ImageSharp
- [x] Excle操作ExcelToObject.Npoi
#### 前端
js Vue3
js Vue3.2
- [x] 异步请求axios
- [x] 图表echarts
- [x] uielement-plus
@@ -134,11 +191,10 @@ js Vue3
- [x] CICDgitlab+Jenkins
- [x] Dockerharbor
****
## 🍌 业务支持模块:
### 业务支持模块:
#### 🍒 RABC权限管理系统持续更新)
RABC权限管理系统正在更新)
采用ruoyi前端
- 用户管理
- 角色管理
@@ -153,112 +209,80 @@ js Vue3
- 定时任务
- 缓存列表
- 服务监控
- WebFirst代码生成工具
#### 🍐 BBS社区论坛系统持续更新
采用vue3前端
- 文章功能
- 板块功能
- 主题功能
- 个人中心
- 授权中心
- 权限管理
**演示截图:**
![输入图片说明](readme/1.png)
![输入图片说明](readme/2.png)
![输入图片说明](readme/3.png)
![输入图片说明](readme/4.png)
![输入图片说明](readme/5.png)
![输入图片说明](readme/6.png)
![输入图片说明](readme/7.png)
![输入图片说明](readme/8.png)
![输入图片说明](readme/9.png)
![输入图片说明](readme/10.png)
![输入图片说明](readme/1696760969217.jpg)
![输入图片说明](readme/1696761014270.jpg)
#### 🍉 演示截图:
<table>
<tr>
<td><img src="readme/101.png"/></td>
<td><img src="readme/102.png"/></td>
</tr>
<tr>
<td><img src="readme/103.png"/></td>
<td><img src="readme/104.png"/></td>
</tr>
</table>
BBS论坛系统持续迭代
- 文章管理
- 评论管理
- 主题管理
- 板块管理
- 点赞管理
- 等等
<table>
<tr>
<td><img src="readme/201.png"/></td>
<td><img src="readme/202.png"/></td>
</tr>
<tr>
<td><img src="readme/203.png"/></td>
<td><img src="readme/204.png"/></td>
</tr>
<tr>
<td><img src="readme/205.png"/></td>
<td><img src="readme/206.png"/></td>
</tr>
</table>
APP移动端系统持续迭代
- 动态查询
- 我的资料
<table>
<tr>
<td><img src="readme/1.png"/></td>
<td><img src="readme/2.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/5.png"/></td>
<td><img src="readme/6.png"/></td>
</tr>
<tr>
<td><img src="readme/7.png"/></td>
<td><img src="readme/8.png"/></td>
</tr>
<tr>
<td><img src="readme/9.png"/></td>
<td><img src="readme/10.png"/></td>
</tr>
<tr>
<td><img src="readme/11.png"/></td>
<td><img src="readme/12.png"/></td>
</tr>
</table>
ERP进销存系统持续迭代
- 供货商管理
- 等等
## 🌶 感谢:
SHOP电商系统持续迭代
- SPU管理
- SKU管理
- 商品规格
- 商品分类
- 等等
****
### 感谢:
**大力支持** Eleven神、Sqlsugar上海杰哥、Gerry、哲学的老张
[橙子]https://ccnetcore.com
[XWen]https://gitee.com/on-wensil
[lzw]https://github.com/yeslode
[朝夕教育]https://www.zhaoxiedu.net
[Sqlsugar老杰哥]https://www.donet5.com/Home/Doc
[车神]微信公众号搜索Dotnet技术进阶
[RuYiAdmin如意老兄]https://gitee.com/pang-mingjun/RuYiAdmin
[ZrAdminNetCore字母老哥]https://gitee.com/izory/ZrAdminNetCore
[Admin.NET]https://gitee.com/zuohuaijun/Admin.NET
[Admin.NET周哥]https://gitee.com/zuohuaijun/Admin.NET
[Furion百小僧]https://furion.baiqian.ltd/
****
## 🌽 联系我们:
### 联系我们:
作者QQ`454313500`2029年之前作者24小时在线时刻保持活跃更新。
QQ交流群官方一群已满、官方二群已满、官方三群`786308927`(已满)、官方四群:`498310311`基本已满)、官方五群:`981136525`新群)
微信交流群:官方微信一群(已满)、官方微信二群
微信交流群:加作者微信 chengzilaoge520 橙子老哥520备注拉群
QQ交流群官方一群已满、官方二群已满、官方三群`786308927`基本已满)、官方四群:`498310311`(新群)
联系作者,这里人人都是顾问
官方网址留言区:[ccnetcore.com](https://ccnetcore.com)
****
## 🍄 FQA:
### FQA:
前往官网查看留言区

View File

@@ -1,30 +0,0 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@@ -1,473 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34202.233
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Web", "src\Yi.Abp.Web\Yi.Abp.Web.csproj", "{15913E44-DA92-44B9-9AC5-E9457EA34BF5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore", "framework\Yi.Framework.SqlSugarCore\Yi.Framework.SqlSugarCore.csproj", "{DC431ECC-C75D-4B01-8B79-4861948179BB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B782C78B-6C17-49E6-A237-3383BA720766}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0D10EEF2-FBAE-4C72-B816-A52823FC299B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "framework", "framework", "{77B949E9-530E-45A5-9657-20F7D5C6875C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "module", "module", "{2317227D-7796-4E7B-BEDB-7CD1CAE7B853}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.SqlSugarCore", "src\Yi.Abp.SqlSugarCore\Yi.Abp.SqlSugarCore.csproj", "{9A7BBA40-28D6-4900-9E1D-D627A516EE72}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Application", "src\Yi.Abp.Application\Yi.Abp.Application.csproj", "{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}"
EndProject
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Domain", "src\Yi.Abp.Domain\Yi.Abp.Domain.csproj", "{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6A5375C6-1D55-4E93-9B19-736F1C68CBC3}"
ProjectSection(SolutionItems) = preProject
common.props = common.props
end.sh = end.sh
logo.png = logo.png
start.sh = start.sh
tool.bat = tool.bat
usings.props = usings.props
version.props = version.props
publish.bat = publish.bat
publish_Demo.bat = publish_Demo.bat
EndProjectSection
EndProject
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Core", "framework\Yi.Framework.Core\Yi.Framework.Core.csproj", "{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Mapster", "framework\Yi.Framework.Mapster\Yi.Framework.Mapster.csproj", "{1995A019-C8AE-467E-B427-ED57D6CBF44F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AspNetCore", "framework\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj", "{F5011C0D-209B-4A98-BBE3-68157503EEF8}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rbac", "rbac", "{9CC7A457-1236-40BA-B47B-E7B710A3F061}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bbs", "bbs", "{E902A945-4F41-4E96-A0DA-9F66CDA22261}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Bbs.Domain.Shared", "module\bbs\Yi.Framework.Bbs.Domain.Shared\Yi.Framework.Bbs.Domain.Shared.csproj", "{EB9349E2-256D-41EB-A345-21635A1361B3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Bbs.Domain", "module\bbs\Yi.Framework.Bbs.Domain\Yi.Framework.Bbs.Domain.csproj", "{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Bbs.Application.Contracts", "module\bbs\Yi.Framework.Bbs.Application.Contracts\Yi.Framework.Bbs.Application.Contracts.csproj", "{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Bbs.Application", "module\bbs\Yi.Framework.Bbs.Application\Yi.Framework.Bbs.Application.csproj", "{AD4EE9E6-F4A3-4139-AF05-71388167DE5B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Bbs.SqlSugarCore", "module\bbs\Yi.Framework.Bbs.SqlSugarCore\Yi.Framework.Bbs.SqlSugarCore.csproj", "{6C86BA71-9F87-4E2C-B467-2950D77DCDFA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "audit-logging", "audit-logging", "{73CCF2C4-B9FD-44AB-8D4B-0A421805B094}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Test", "test\Yi.Abp.Test\Yi.Abp.Test.csproj", "{68627BC2-F049-4C69-AD17-81DF9478E8CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tenant-management", "tenant-management", "{499A8C71-7892-42D0-A77E-48756E1EFF16}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "code-gen", "code-gen", "{4FFE7212-21F2-476D-B628-3C65E6C5075E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application", "module\code-gen\Yi.Framework.CodeGen.Application\Yi.Framework.CodeGen.Application.csproj", "{97EC40D7-DBFA-467A-98CB-221AF27B14F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application.Contracts", "module\code-gen\Yi.Framework.CodeGen.Application.Contracts\Yi.Framework.CodeGen.Application.Contracts.csproj", "{882BC563-2F75-4B95-AC96-F4BF23F5E69D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain", "module\code-gen\Yi.Framework.CodeGen.Domain\Yi.Framework.CodeGen.Domain.csproj", "{85CB8517-2B80-42D8-B954-081079AC9BA0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain.Shared", "module\code-gen\Yi.Framework.CodeGen.Domain.Shared\Yi.Framework.CodeGen.Domain.Shared.csproj", "{EEFF0F05-2709-4151-A8CE-667935CEAE0B}"
EndProject
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.SqlSugarCore", "module\code-gen\Yi.Framework.CodeGen.SqlSugarCore\Yi.Framework.CodeGen.SqlSugarCore.csproj", "{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "client", "client", "{8B27846A-043D-4F2F-8140-5CEC9D1863B5}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "setting-management", "setting-management", "{8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}"
EndProject
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}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chat-hub", "chat-hub", "{D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.ChatHub.Application.Contracts", "module\chat-hub\Yi.Framework.ChatHub.Application.Contracts\Yi.Framework.ChatHub.Application.Contracts.csproj", "{65D4D033-5504-44B9-B152-0172ACD64CE6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.ChatHub.Domain.Shared", "module\chat-hub\Yi.Framework.ChatHub.Domain.Shared\Yi.Framework.ChatHub.Domain.Shared.csproj", "{DEEC0B15-190C-4464-B469-C45C6563C592}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.ChatHub.SqlSugarCore", "module\chat-hub\Yi.Framework.ChatHub.SqlSugarCore\Yi.Framework.ChatHub.SqlSugarCore.csproj", "{E476D266-8FB2-4D6B-AE2B-F0D279D4264E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.ChatHub.Domain", "module\chat-hub\Yi.Framework.ChatHub.Domain\Yi.Framework.ChatHub.Domain.csproj", "{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.ChatHub.Application", "module\chat-hub\Yi.Framework.ChatHub.Application\Yi.Framework.ChatHub.Application.csproj", "{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Rbac.Test", "test\Yi.Framework.Rbac.Test\Yi.Framework.Rbac.Test.csproj", "{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tool", "tool", "{084CBEEC-5D37-4716-B9C7-D80D6960DFF4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool", "tool\Yi.Abp.Tool\Yi.Abp.Tool.csproj", "{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.Web", "tool\Yi.Abp.Tool.Web\Yi.Abp.Tool.Web.csproj", "{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.Application", "tool\Yi.Abp.Tool.Application\Yi.Abp.Tool.Application.csproj", "{776590BA-B900-4C8B-986A-5B721FA4B306}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.Application.Contracts", "tool\Yi.Abp.Tool.Application.Contracts\Yi.Abp.Tool.Application.Contracts.csproj", "{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.Domain", "tool\Yi.Abp.Tool.Domain\Yi.Abp.Tool.Domain.csproj", "{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.Domain.Shared", "tool\Yi.Abp.Tool.Domain.Shared\Yi.Abp.Tool.Domain.Shared.csproj", "{4AE84CDE-2A47-4D68-8E93-86193F72E4E8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.HttpApi.Client", "tool\Yi.Abp.Tool.HttpApi.Client\Yi.Abp.Tool.HttpApi.Client.csproj", "{C8F97775-D903-4365-A4FF-3DA97E318CD2}"
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15913E44-DA92-44B9-9AC5-E9457EA34BF5}.Release|Any CPU.Build.0 = Release|Any CPU
{DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC431ECC-C75D-4B01-8B79-4861948179BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC431ECC-C75D-4B01-8B79-4861948179BB}.Release|Any CPU.Build.0 = Release|Any CPU
{9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A7BBA40-28D6-4900-9E1D-D627A516EE72}.Release|Any CPU.Build.0 = Release|Any CPU
{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{746DBBD6-23E8-4D5D-9D23-E2902BE338BD}.Release|Any CPU.Build.0 = Release|Any CPU
{51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51EEBF59-3D37-4681-981D-56F8D8F8968D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51EEBF59-3D37-4681-981D-56F8D8F8968D}.Release|Any CPU.Build.0 = Release|Any CPU
{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD}.Release|Any CPU.Build.0 = Release|Any CPU
{F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4D5A496-BFBE-470B-A05B-CB5823B47E72}.Release|Any CPU.Build.0 = Release|Any CPU
{FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD6D6860-3753-4747-8A26-977E4A3001F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD6D6860-3753-4747-8A26-977E4A3001F9}.Release|Any CPU.Build.0 = Release|Any CPU
{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5}.Release|Any CPU.Build.0 = Release|Any CPU
{1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1995A019-C8AE-467E-B427-ED57D6CBF44F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1995A019-C8AE-467E-B427-ED57D6CBF44F}.Release|Any CPU.Build.0 = Release|Any CPU
{F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5011C0D-209B-4A98-BBE3-68157503EEF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5011C0D-209B-4A98-BBE3-68157503EEF8}.Release|Any CPU.Build.0 = Release|Any CPU
{0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A8296A3-C11F-4F13-8E49-6BC8188D4804}.Release|Any CPU.Build.0 = Release|Any CPU
{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE}.Release|Any CPU.Build.0 = Release|Any CPU
{1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C360956-8CD8-407E-B87F-D0BD57068EB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C360956-8CD8-407E-B87F-D0BD57068EB9}.Release|Any CPU.Build.0 = Release|Any CPU
{4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F02B08D-5FE2-460D-BCA5-DA565151AE30}.Release|Any CPU.Build.0 = Release|Any CPU
{C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C04D3F71-1557-46D0-B810-97B1FBB6AB73}.Release|Any CPU.Build.0 = Release|Any CPU
{A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2BB899D-4F9A-4184-81BD-94B938E2AB03}.Release|Any CPU.Build.0 = Release|Any CPU
{4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4503A2F9-139D-4CBC-AF11-689C34F0D77B}.Release|Any CPU.Build.0 = Release|Any CPU
{EB9349E2-256D-41EB-A345-21635A1361B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB9349E2-256D-41EB-A345-21635A1361B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB9349E2-256D-41EB-A345-21635A1361B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB9349E2-256D-41EB-A345-21635A1361B3}.Release|Any CPU.Build.0 = Release|Any CPU
{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7}.Release|Any CPU.Build.0 = Release|Any CPU
{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20}.Release|Any CPU.Build.0 = Release|Any CPU
{AD4EE9E6-F4A3-4139-AF05-71388167DE5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD4EE9E6-F4A3-4139-AF05-71388167DE5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD4EE9E6-F4A3-4139-AF05-71388167DE5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD4EE9E6-F4A3-4139-AF05-71388167DE5B}.Release|Any CPU.Build.0 = Release|Any CPU
{6C86BA71-9F87-4E2C-B467-2950D77DCDFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C86BA71-9F87-4E2C-B467-2950D77DCDFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C86BA71-9F87-4E2C-B467-2950D77DCDFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C86BA71-9F87-4E2C-B467-2950D77DCDFA}.Release|Any CPU.Build.0 = Release|Any CPU
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3}.Release|Any CPU.Build.0 = Release|Any CPU
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.Build.0 = Release|Any CPU
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Release|Any CPU.Build.0 = Release|Any CPU
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556}.Release|Any CPU.Build.0 = Release|Any CPU
{54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54D8E2BC-591C-4344-A58E-874D49C00B41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54D8E2BC-591C-4344-A58E-874D49C00B41}.Release|Any CPU.Build.0 = Release|Any CPU
{EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFD13211-17B5-400A-B99A-9F6F4E520C1E}.Release|Any CPU.Build.0 = Release|Any CPU
{9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C8C3C53-3DCE-4516-867E-228858E61B26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C8C3C53-3DCE-4516-867E-228858E61B26}.Release|Any CPU.Build.0 = Release|Any CPU
{17816837-E53B-486B-B796-53C601FE6CD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17816837-E53B-486B-B796-53C601FE6CD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17816837-E53B-486B-B796-53C601FE6CD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17816837-E53B-486B-B796-53C601FE6CD9}.Release|Any CPU.Build.0 = Release|Any CPU
{FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA735055-CBDD-4EFD-B84B-85810DA1425E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA735055-CBDD-4EFD-B84B-85810DA1425E}.Release|Any CPU.Build.0 = Release|Any CPU
{97EC40D7-DBFA-467A-98CB-221AF27B14F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97EC40D7-DBFA-467A-98CB-221AF27B14F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97EC40D7-DBFA-467A-98CB-221AF27B14F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97EC40D7-DBFA-467A-98CB-221AF27B14F2}.Release|Any CPU.Build.0 = Release|Any CPU
{882BC563-2F75-4B95-AC96-F4BF23F5E69D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{882BC563-2F75-4B95-AC96-F4BF23F5E69D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{882BC563-2F75-4B95-AC96-F4BF23F5E69D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{882BC563-2F75-4B95-AC96-F4BF23F5E69D}.Release|Any CPU.Build.0 = Release|Any CPU
{85CB8517-2B80-42D8-B954-081079AC9BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85CB8517-2B80-42D8-B954-081079AC9BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85CB8517-2B80-42D8-B954-081079AC9BA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85CB8517-2B80-42D8-B954-081079AC9BA0}.Release|Any CPU.Build.0 = Release|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Release|Any CPU.Build.0 = Release|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.Build.0 = Release|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Release|Any CPU.Build.0 = Release|Any CPU
{6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B554DCC-3A81-4624-9141-4E39365ADA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B554DCC-3A81-4624-9141-4E39365ADA35}.Release|Any CPU.Build.0 = Release|Any CPU
{2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D23B44A-DFA3-4C36-8516-4F5AE442403C}.Release|Any CPU.Build.0 = Release|Any CPU
{00E49781-C6A0-491C-86A1-46F685C90915}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00E49781-C6A0-491C-86A1-46F685C90915}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00E49781-C6A0-491C-86A1-46F685C90915}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00E49781-C6A0-491C-86A1-46F685C90915}.Release|Any CPU.Build.0 = Release|Any CPU
{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF}.Release|Any CPU.Build.0 = Release|Any CPU
{495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{495C4643-39D4-46E7-BDC8-237589627BE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{495C4643-39D4-46E7-BDC8-237589627BE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{495C4643-39D4-46E7-BDC8-237589627BE4}.Release|Any CPU.Build.0 = Release|Any CPU
{65D4D033-5504-44B9-B152-0172ACD64CE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65D4D033-5504-44B9-B152-0172ACD64CE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65D4D033-5504-44B9-B152-0172ACD64CE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65D4D033-5504-44B9-B152-0172ACD64CE6}.Release|Any CPU.Build.0 = Release|Any CPU
{DEEC0B15-190C-4464-B469-C45C6563C592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DEEC0B15-190C-4464-B469-C45C6563C592}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DEEC0B15-190C-4464-B469-C45C6563C592}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DEEC0B15-190C-4464-B469-C45C6563C592}.Release|Any CPU.Build.0 = Release|Any CPU
{E476D266-8FB2-4D6B-AE2B-F0D279D4264E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E476D266-8FB2-4D6B-AE2B-F0D279D4264E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E476D266-8FB2-4D6B-AE2B-F0D279D4264E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E476D266-8FB2-4D6B-AE2B-F0D279D4264E}.Release|Any CPU.Build.0 = Release|Any CPU
{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47}.Release|Any CPU.Build.0 = Release|Any CPU
{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D}.Release|Any CPU.Build.0 = Release|Any CPU
{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07}.Release|Any CPU.Build.0 = Release|Any CPU
{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F}.Release|Any CPU.Build.0 = Release|Any CPU
{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1}.Release|Any CPU.Build.0 = Release|Any CPU
{776590BA-B900-4C8B-986A-5B721FA4B306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{776590BA-B900-4C8B-986A-5B721FA4B306}.Debug|Any CPU.Build.0 = Debug|Any CPU
{776590BA-B900-4C8B-986A-5B721FA4B306}.Release|Any CPU.ActiveCfg = Release|Any CPU
{776590BA-B900-4C8B-986A-5B721FA4B306}.Release|Any CPU.Build.0 = Release|Any CPU
{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675}.Release|Any CPU.Build.0 = Release|Any CPU
{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8}.Release|Any CPU.Build.0 = Release|Any CPU
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8}.Release|Any CPU.Build.0 = Release|Any CPU
{C8F97775-D903-4365-A4FF-3DA97E318CD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8F97775-D903-4365-A4FF-3DA97E318CD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8F97775-D903-4365-A4FF-3DA97E318CD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8F97775-D903-4365-A4FF-3DA97E318CD2}.Release|Any CPU.Build.0 = Release|Any CPU
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.Build.0 = Release|Any CPU
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{15913E44-DA92-44B9-9AC5-E9457EA34BF5} = {B782C78B-6C17-49E6-A237-3383BA720766}
{DC431ECC-C75D-4B01-8B79-4861948179BB} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{9A7BBA40-28D6-4900-9E1D-D627A516EE72} = {B782C78B-6C17-49E6-A237-3383BA720766}
{746DBBD6-23E8-4D5D-9D23-E2902BE338BD} = {B782C78B-6C17-49E6-A237-3383BA720766}
{51EEBF59-3D37-4681-981D-56F8D8F8968D} = {B782C78B-6C17-49E6-A237-3383BA720766}
{7B15C198-538A-44ED-A6AA-3A0FEAA1D2BD} = {B782C78B-6C17-49E6-A237-3383BA720766}
{F4D5A496-BFBE-470B-A05B-CB5823B47E72} = {B782C78B-6C17-49E6-A237-3383BA720766}
{FD6D6860-3753-4747-8A26-977E4A3001F9} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{ECE874D4-F882-4EF4-84A6-A842D9B8FBC5} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{1995A019-C8AE-467E-B427-ED57D6CBF44F} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{F5011C0D-209B-4A98-BBE3-68157503EEF8} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{0A8296A3-C11F-4F13-8E49-6BC8188D4804} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{F0141C17-0EBD-4261-98D5-1C5B7BC1DFEE} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{9CC7A457-1236-40BA-B47B-E7B710A3F061} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{1C360956-8CD8-407E-B87F-D0BD57068EB9} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
{4F02B08D-5FE2-460D-BCA5-DA565151AE30} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
{C04D3F71-1557-46D0-B810-97B1FBB6AB73} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
{A2BB899D-4F9A-4184-81BD-94B938E2AB03} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
{4503A2F9-139D-4CBC-AF11-689C34F0D77B} = {9CC7A457-1236-40BA-B47B-E7B710A3F061}
{E902A945-4F41-4E96-A0DA-9F66CDA22261} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{EB9349E2-256D-41EB-A345-21635A1361B3} = {E902A945-4F41-4E96-A0DA-9F66CDA22261}
{4EABBC84-BCED-46C1-8CF1-62A7B8081ED7} = {E902A945-4F41-4E96-A0DA-9F66CDA22261}
{7E569FD9-B1AB-4848-8AB7-FD9EFA1DBA20} = {E902A945-4F41-4E96-A0DA-9F66CDA22261}
{AD4EE9E6-F4A3-4139-AF05-71388167DE5B} = {E902A945-4F41-4E96-A0DA-9F66CDA22261}
{6C86BA71-9F87-4E2C-B467-2950D77DCDFA} = {E902A945-4F41-4E96-A0DA-9F66CDA22261}
{73CCF2C4-B9FD-44AB-8D4B-0A421805B094} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
{791AC2FA-50D3-4408-8D68-31DA72F608BE} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{68627BC2-F049-4C69-AD17-81DF9478E8CE} = {0D10EEF2-FBAE-4C72-B816-A52823FC299B}
{499A8C71-7892-42D0-A77E-48756E1EFF16} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
{54D8E2BC-591C-4344-A58E-874D49C00B41} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
{EFD13211-17B5-400A-B99A-9F6F4E520C1E} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
{9C8C3C53-3DCE-4516-867E-228858E61B26} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
{17816837-E53B-486B-B796-53C601FE6CD9} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
{FA735055-CBDD-4EFD-B84B-85810DA1425E} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
{4FFE7212-21F2-476D-B628-3C65E6C5075E} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{97EC40D7-DBFA-467A-98CB-221AF27B14F2} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{882BC563-2F75-4B95-AC96-F4BF23F5E69D} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{85CB8517-2B80-42D8-B954-081079AC9BA0} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{EEFF0F05-2709-4151-A8CE-667935CEAE0B} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{6B554DCC-3A81-4624-9141-4E39365ADA35} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
{2D23B44A-DFA3-4C36-8516-4F5AE442403C} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
{00E49781-C6A0-491C-86A1-46F685C90915} = {8B27846A-043D-4F2F-8140-5CEC9D1863B5}
{8C68059E-F3B1-4D28-A1C9-A5830F53E5D3} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{6FEE0EB3-EAD2-47F8-B6FC-3D0FD3CCABFF} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
{495C4643-39D4-46E7-BDC8-237589627BE4} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
{D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
{65D4D033-5504-44B9-B152-0172ACD64CE6} = {D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}
{DEEC0B15-190C-4464-B469-C45C6563C592} = {D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}
{E476D266-8FB2-4D6B-AE2B-F0D279D4264E} = {D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}
{C2DCA2FD-BFB4-4E76-967B-0AF8CC4F4D47} = {D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}
{B7A1A8F3-CFA6-4ECF-A707-0F33FE0A6F1D} = {D8CDDE99-3684-4EED-A5E5-87F2AF4C78AB}
{9ECF0841-53BE-4FD8-95D1-A7223C7F3A07} = {0D10EEF2-FBAE-4C72-B816-A52823FC299B}
{4FEBBDD9-E4F4-4BAF-8599-E2D57C08A74F} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{2CE51D4C-1EF9-462B-BA14-7EA01A7E4AF1} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{776590BA-B900-4C8B-986A-5B721FA4B306} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{3A3AF1ED-FC7F-48CF-8ACE-9D50426B4675} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{68F73B7B-0F8A-41C1-8092-6D6FFAED32F8} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{C8F97775-D903-4365-A4FF-3DA97E318CD2} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
{862CA181-BEE6-4870-82D2-B662E527ED8C} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {23D6FBC9-C970-4641-BC1E-2AEA59F51C18}
EndGlobalSection
EndGlobal

View File

@@ -1,33 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Yi.Abp.Client.Console;
using Yi.Framework.Rbac.Application.Contracts.IServices;
try
{
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices(async (host, service) =>
{
await service.AddApplicationAsync<YiAbpClientConsoleModule>();
})
.UseAutofac()
.Build();
//控制台直接调用
var account = host.Services.GetRequiredService<IAccountService>();
//获取验证码
var data1 = await account.GetCaptchaImageAsync();
//登录
var data2 = await account.PostLoginAsync(new Yi.Framework.Rbac.Application.Contracts.Dtos.Account.LoginInputVo { UserName = "cc", Password = "123456", Code = string.Empty, Uuid = string.Empty });
host.Run();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}

View File

@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Abp.HttpApi.Client\Yi.Abp.HttpApi.Client.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Modularity;
using Yi.Abp.HttpApi.Client;
namespace Yi.Abp.Client.Console
{
[DependsOn(typeof(YiAbpHttpApiClientModule))]
public class YiAbpClientConsoleModule:AbpModule
{
}
}

View File

@@ -1,37 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
using Yi.Framework.Rbac.Application.Contracts.IServices;
namespace Yi.Abp.Client.WebApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
private readonly ILogger<AccountController> _logger;
private IAccountService _accountService;
public AccountController(ILogger<AccountController> logger, IAccountService accountService)
{
_logger = logger;
_accountService = accountService;
}
[HttpPost("my-login")]
public async Task<IActionResult> Login(LoginInputVo input)
{
await _accountService.PostLoginAsync(input);
return Ok();
}
[HttpGet("my-captcha-image")]
public async Task<IActionResult> CaptchaImageAsync()
{
var output = await _accountService.GetCaptchaImageAsync();
return Ok(output);
}
}
}

View File

@@ -1,28 +0,0 @@
using Autofac.Core;
using Yi.Abp.Client.WebApi;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Host.UseAutofac();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
await builder.Services.AddApplicationAsync<YiAbpClientWebApiModule>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@@ -1,41 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35597",
"sslPort": 44322
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7108;http://localhost:5002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Abp.HttpApi.Client\Yi.Abp.HttpApi.Client.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,6 +0,0 @@
@Yi.Abp.Client.WebApi_HostAddress = http://localhost:5002
GET {{Yi.Abp.Client.WebApi_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -1,10 +0,0 @@
using Volo.Abp.Modularity;
using Yi.Abp.HttpApi.Client;
namespace Yi.Abp.Client.WebApi
{
[DependsOn(typeof(YiAbpHttpApiClientModule))]
public class YiAbpClientWebApiModule:AbpModule
{
}
}

View File

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

View File

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

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Volo.Abp.Http.Client" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Yi.Abp.Application.Contracts\Yi.Abp.Application.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,32 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
using Yi.Framework.Rbac.Application.Contracts;
namespace Yi.Abp.HttpApi.Client
{
[DependsOn(typeof(AbpHttpClientModule),
typeof(AbpAutofacModule),
typeof(YiFrameworkRbacApplicationContractsModule))]
public class YiAbpHttpApiClientModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//创建动态客户端代理
context.Services.AddHttpClientProxies(
typeof(YiFrameworkRbacApplicationContractsModule).Assembly
);
Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:19001");
});
}
}
}

View File

@@ -1,39 +0,0 @@
<Project>
<Import Project="usings.props" />
<Import Project="version.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>1.0.0</Version>
<NoWarn>$(NoWarn);CS1591;CS8618;CS1998;CS8604;CS8620;CS8600;CS8602</NoWarn>
<AbpProjectType>app</AbpProjectType>
<PublishDocumentationFiles>true</PublishDocumentationFiles>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile">
<PropertyGroup>
<NoWarn>$(NoWarn);0436</NoWarn>
</PropertyGroup>
</Target>
<ItemGroup>
<Content Remove="$(UserProfile)\.nuget\packages\*\*\contentFiles\any\*\*.abppkg*.json" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>

View File

@@ -1,3 +0,0 @@
#!/bin/bash
kill -9 $(lsof -t -i:19001)
echo "Yi-进程已关闭"

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.AspNetCore.Authentication.OAuth
{
public class AuthenticationConstants
{
public const string OpenId = "urn:openid";
public const string AccessToken = "urn:access_token";
public const string Name = "urn:name";
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth;
namespace Yi.Framework.AspNetCore.Authentication.OAuth
{
public class AuthenticationOAuthOptions:OAuthOptions
{
public string RedirectUri { get; set; }
}
}

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
namespace Yi.Framework.AspNetCore.Authentication.OAuth
{
public class AuthticationErrCodeModel
{
public string error { get; set; }
public string error_description { get; set; }
public static void VerifyErrResponse(string content)
{
var model = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthticationErrCodeModel>(content);
if (model.error != null)
{
throw new Exception($"第三方授权返回错误,错误码:【{model.error}】,错误详情:【{model.error_description}】");
}
}
}
}

View File

@@ -1,19 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
/// <summary>
/// Contains constants specific to the <see cref="GiteeAuthenticationHandler"/>.
/// </summary>
public static class GiteeAuthenticationConstants
{
public static class Claims
{
public const string Url = "urn:gitee:url";
public const string AvatarUrl = "urn:gitee:avatarUrl";
}
}

View File

@@ -1,53 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
/// <summary>
/// Default values used by the Gitee authentication middleware.
/// </summary>
public static class GiteeAuthenticationDefaults
{
/// <summary>
/// Default value for <see cref="AuthenticationScheme.Name"/>.
/// </summary>
public const string AuthenticationScheme = "Gitee";
/// <summary>
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
/// </summary>
public static readonly string DisplayName = "Gitee";
/// <summary>
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
/// </summary>
public static readonly string Issuer = "Gitee";
/// <summary>
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
/// </summary>
public static readonly string CallbackPath = "/signin-gitee";
/// <summary>
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
/// </summary>
public static readonly string AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
/// <summary>
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
/// </summary>
public static readonly string TokenEndpoint = "https://gitee.com/oauth/token";
/// <summary>
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
/// </summary>
public static readonly string UserInformationEndpoint = "https://gitee.com/api/v5/user";
/// <summary>
/// Default value for <see cref="GiteeAuthenticationOptions.UserEmailsEndpoint"/>.
/// </summary>
public static readonly string UserEmailsEndpoint = "https://gitee.com/api/v5/emails";
}

View File

@@ -1,75 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
/// <summary>
/// Extension methods to add Gitee authentication capabilities to an HTTP application pipeline.
/// </summary>
public static class GiteeAuthenticationExtensions
{
/// <summary>
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddGitee([NotNull] this AuthenticationBuilder builder)
{
return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, options => { });
}
/// <summary>
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddGitee(
[NotNull] this AuthenticationBuilder builder,
[NotNull] Action<GiteeAuthenticationOptions> configuration)
{
return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, configuration);
}
/// <summary>
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="scheme">The authentication scheme associated with this instance.</param>
/// <param name="configuration">The delegate used to configure the Gitee options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddGitee(
[NotNull] this AuthenticationBuilder builder,
[NotNull] string scheme,
[NotNull] Action<GiteeAuthenticationOptions> configuration)
{
return builder.AddGitee(scheme, GiteeAuthenticationDefaults.DisplayName, configuration);
}
/// <summary>
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="scheme">The authentication scheme associated with this instance.</param>
/// <param name="caption">The optional display name associated with this instance.</param>
/// <param name="configuration">The delegate used to configure the Gitee options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddGitee(
[NotNull] this AuthenticationBuilder builder,
[NotNull] string scheme,
[CanBeNull] string caption,
[NotNull] Action<GiteeAuthenticationOptions> configuration)
{
return builder.AddScheme<GiteeAuthenticationOptions, GiteeAuthenticationHandler>(scheme, caption, configuration);
}
}

View File

@@ -1,49 +0,0 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
{
public class GiteeAuthenticationHandler : OauthAuthenticationHandler<GiteeAuthenticationOptions>
{
public GiteeAuthenticationHandler(IOptionsMonitor<GiteeAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
{
}
public override string AuthenticationSchemeNmae => GiteeAuthenticationDefaults.AuthenticationScheme;
protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
{
//获取 accessToken
var tokenQueryKv = new List<KeyValuePair<string, string?>>()
{
new KeyValuePair<string, string?>("grant_type","authorization_code"),
new KeyValuePair<string, string?>("client_id",Options.ClientId),
new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
new KeyValuePair<string, string?>("code",code)
};
var tokenModel = await SendHttpRequestAsync<GiteeAuthticationcationTokenResponse>(GiteeAuthenticationDefaults.TokenEndpoint, tokenQueryKv,HttpMethod.Post);
//获取 userInfo
var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
{
new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
};
var userInfoMdoel = await SendHttpRequestAsync<GiteeAuthticationcationUserInfoResponse>(GiteeAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
List<Claim> claims = new List<Claim>()
{
new Claim(Claims.AvatarUrl, userInfoMdoel.avatar_url),
new Claim(Claims.Url, userInfoMdoel.url),
new Claim(AuthenticationConstants.OpenId,userInfoMdoel.id.ToString()),
new Claim(AuthenticationConstants.Name, userInfoMdoel.name),
new Claim(AuthenticationConstants.AccessToken, tokenModel.access_token)
};
return claims;
}
}
}

View File

@@ -1,44 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
/// <summary>
/// Defines a set of options used by <see cref="GiteeAuthenticationHandler"/>.
/// </summary>
public class GiteeAuthenticationOptions : AuthenticationOAuthOptions
{
public GiteeAuthenticationOptions()
{
ClaimsIssuer = GiteeAuthenticationDefaults.Issuer;
CallbackPath = GiteeAuthenticationDefaults.CallbackPath;
AuthorizationEndpoint = GiteeAuthenticationDefaults.AuthorizationEndpoint;
TokenEndpoint = GiteeAuthenticationDefaults.TokenEndpoint;
UserInformationEndpoint = GiteeAuthenticationDefaults.UserInformationEndpoint;
UserEmailsEndpoint = GiteeAuthenticationDefaults.UserEmailsEndpoint;
Scope.Add("user_info");
Scope.Add("emails");
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
ClaimActions.MapJsonKey(ClaimTypes.Name, "login");
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
ClaimActions.MapJsonKey(Claims.Url, "url");
}
/// <summary>
/// Gets or sets the address of the endpoint exposing
/// the email addresses associated with the logged in user.
/// </summary>
public string UserEmailsEndpoint { get; set; }
}

View File

@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
{
public class GiteeAuthticationcationTokenResponse
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string refresh_token { get; set; }
public string scope { get; set; }
public long created_at { get; set; }
}
public class GiteeAuthticationcationOpenIdResponse
{
public string client_id { get; set; }
public string openid { get; set; }
}
public class GiteeAuthticationcationUserInfoResponse
{
/// <summary>
/// 也可以等于openId
/// </summary>
public int id { get; set; }
public string login { get; set; }
public string name { get; set; }
public string avatar_url { get; set; }
public string url { get; set; }
public string html_url { get; set; }
public string remark { get; set; }
public string followers_url { get; set; }
public string following_url { get; set; }
public string gists_url { get; set; }
public string starred_url { get; set; }
public string subscriptions_url { get; set; }
public string organizations_url { get; set; }
public string repos_url { get; set; }
public string events_url { get; set; }
public string received_events_url { get; set; }
public string type { get; set; }
public string blog { get; set; }
public string weibo { get; set; }
public string bio { get; set; }
public int public_repos { get; set; }
public int public_gists { get; set; }
public int followers { get; set; }
public int following { get; set; }
public int stared { get; set; }
public int watched { get; set; }
public DateTime created_at { get; set; }
public DateTime updated_at { get; set; }
public string email { get; set; }
}
}

View File

@@ -1,96 +0,0 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Yi.Framework.AspNetCore.Authentication.OAuth
{
public abstract class OauthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
{
public abstract string AuthenticationSchemeNmae { get; }
private AuthenticationScheme _scheme;
public OauthAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder)
{
HttpClientFactory = httpClientFactory;
HttpClient = HttpClientFactory.CreateClient();
}
protected IHttpClientFactory HttpClientFactory { get; }
protected HttpClient HttpClient { get; }
/// <summary>
/// 生成认证票据
/// </summary>
/// <returns></returns>
private AuthenticationTicket TicketConver(List<Claim> claims)
{
var claimsIdentity = new ClaimsIdentity(claims.ToArray(), AuthenticationSchemeNmae);
var principal = new ClaimsPrincipal(claimsIdentity);
return new AuthenticationTicket(principal, AuthenticationSchemeNmae);
}
protected async Task<HttpModel> SendHttpRequestAsync<HttpModel>(string url, IEnumerable<KeyValuePair<string, string?>> query, HttpMethod? httpMethod = null)
{
httpMethod = httpMethod ?? HttpMethod.Get;
var queryUrl = QueryHelpers.AddQueryString(url, query);
HttpResponseMessage response = null;
if (httpMethod == HttpMethod.Get)
{
response = await HttpClient.GetAsync(queryUrl);
}
else if (httpMethod == HttpMethod.Post)
{
response = await HttpClient.PostAsync(queryUrl, null);
}
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"授权服务器请求错误,请求地址:{queryUrl},错误信息:{content}");
}
VerifyErrResponse(content);
var model = Newtonsoft.Json.JsonConvert.DeserializeObject<HttpModel>(content);
return model!;
}
protected virtual void VerifyErrResponse(string content)
{
AuthticationErrCodeModel.VerifyErrResponse(content);
}
protected abstract Task<List<Claim>> GetAuthTicketAsync(string code);
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.Query.ContainsKey("code"))
{
return AuthenticateResult.Fail("回调未包含code参数");
}
var code = Context.Request.Query["code"].ToString();
List<Claim> authTicket = null;
try
{
authTicket = await GetAuthTicketAsync(code);
}
catch (Exception ex)
{
return AuthenticateResult.Fail(ex.Message ?? "未知错误");
}
//成功
var result = AuthenticateResult.Success(TicketConver(authTicket));
return result;
}
}
}

View File

@@ -1,23 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
/// <summary>
/// Contains constants specific to the <see cref="QQAuthenticationHandler"/>.
/// </summary>
public static class QQAuthenticationConstants
{
public static class Claims
{
public const string AvatarFullUrl = "urn:qq:avatar_full";
public const string AvatarUrl = "urn:qq:avatar";
public const string PictureFullUrl = "urn:qq:picture_full";
public const string PictureMediumUrl = "urn:qq:picture_medium";
public const string PictureUrl = "urn:qq:picture";
public const string UnionId = "urn:qq:unionid";
}
}

View File

@@ -1,53 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
/// <summary>
/// Default values for QQ authentication.
/// </summary>
public static class QQAuthenticationDefaults
{
/// <summary>
/// Default value for <see cref="AuthenticationScheme.Name"/>.
/// </summary>
public const string AuthenticationScheme = "QQ";
/// <summary>
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
/// </summary>
public static readonly string DisplayName = "QQ";
/// <summary>
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
/// </summary>
public static readonly string Issuer = "QQ";
/// <summary>
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
/// </summary>
public static readonly string CallbackPath = "/signin-qq";
/// <summary>
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
/// </summary>
public static readonly string AuthorizationEndpoint = "https://graph.qq.com/oauth2.0/authorize";
/// <summary>
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
/// </summary>
public static readonly string TokenEndpoint = "https://graph.qq.com/oauth2.0/token";
/// <summary>
/// Default value for <see cref="QQAuthenticationOptions.UserIdentificationEndpoint"/>.
/// </summary>
public static readonly string UserIdentificationEndpoint = "https://graph.qq.com/oauth2.0/me";
/// <summary>
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
/// </summary>
public static readonly string UserInformationEndpoint = "https://graph.qq.com/user/get_user_info";
}

View File

@@ -1,77 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
/// <summary>
/// Extension methods to add QQ authentication capabilities to an HTTP application pipeline.
/// </summary>
public static class QQAuthenticationExtensions
{
/// <summary>
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddQQ([NotNull] this AuthenticationBuilder builder)
{
return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, options => { });
}
/// <summary>
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddQQ(
[NotNull] this AuthenticationBuilder builder,
[NotNull] Action<QQAuthenticationOptions> configuration)
{
return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, configuration);
}
/// <summary>
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="scheme">The authentication scheme associated with this instance.</param>
/// <param name="configuration">The delegate used to configure the QQ options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddQQ(
[NotNull] this AuthenticationBuilder builder,
[NotNull] string scheme,
[NotNull] Action<QQAuthenticationOptions> configuration)
{
return builder.AddQQ(scheme, QQAuthenticationDefaults.DisplayName, configuration);
}
/// <summary>
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
/// </summary>
/// <param name="builder">The authentication builder.</param>
/// <param name="scheme">The authentication scheme associated with this instance.</param>
/// <param name="caption">The optional display name associated with this instance.</param>
/// <param name="configuration">The delegate used to configure the QQ options.</param>
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
public static AuthenticationBuilder AddQQ(
[NotNull] this AuthenticationBuilder builder,
[NotNull] string scheme,
[CanBeNull] string caption,
[NotNull] Action<QQAuthenticationOptions> configuration)
{
return builder.AddScheme<QQAuthenticationOptions, QQAuthenticationHandler>(scheme, caption, configuration);
}
}

View File

@@ -1,64 +0,0 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
{
public class QQAuthenticationHandler : OauthAuthenticationHandler<QQAuthenticationOptions>
{
public QQAuthenticationHandler(IOptionsMonitor<QQAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
{
}
public override string AuthenticationSchemeNmae => QQAuthenticationDefaults.AuthenticationScheme;
protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
{
//获取 accessToken
var tokenQueryKv = new List<KeyValuePair<string, string?>>()
{
new KeyValuePair<string, string?>("grant_type","authorization_code"),
new KeyValuePair<string, string?>("client_id",Options.ClientId),
new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
new KeyValuePair<string, string?>("fmt","json"),
new KeyValuePair<string, string?>("need_openid","1"),
new KeyValuePair<string, string?>("code",code)
};
var tokenModel = await SendHttpRequestAsync<QQAuthticationcationTokenResponse>(QQAuthenticationDefaults.TokenEndpoint, tokenQueryKv);
//获取 userInfo
var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
{
new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
new KeyValuePair<string, string?>("oauth_consumer_key",Options.ClientId),
new KeyValuePair<string, string?>("openid",tokenModel.openid),
};
var userInfoMdoel = await SendHttpRequestAsync<QQAuthticationcationUserInfoResponse>(QQAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
List<Claim> claims = new List<Claim>()
{
new Claim(Claims.AvatarFullUrl, userInfoMdoel.figureurl_qq_2),
new Claim(Claims.AvatarUrl, userInfoMdoel.figureurl_qq_1),
new Claim(Claims.PictureFullUrl, userInfoMdoel.figureurl_2),
new Claim(Claims.PictureMediumUrl, userInfoMdoel.figureurl_qq_1),
new Claim(Claims.PictureUrl, userInfoMdoel.figureurl),
new Claim(AuthenticationConstants.OpenId, tokenModel.openid),
new Claim(AuthenticationConstants.Name, userInfoMdoel.nickname),
new Claim(AuthenticationConstants.AccessToken, tokenModel.access_token),
};
return claims;
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
/// <summary>
/// Defines a set of options used by <see cref="QQAuthenticationHandler"/>.
/// </summary>
public class QQAuthenticationOptions : AuthenticationOAuthOptions
{
public QQAuthenticationOptions()
{
ClaimsIssuer = QQAuthenticationDefaults.Issuer;
CallbackPath = QQAuthenticationDefaults.CallbackPath;
AuthorizationEndpoint = QQAuthenticationDefaults.AuthorizationEndpoint;
TokenEndpoint = QQAuthenticationDefaults.TokenEndpoint;
UserIdentificationEndpoint = QQAuthenticationDefaults.UserIdentificationEndpoint;
UserInformationEndpoint = QQAuthenticationDefaults.UserInformationEndpoint;
Scope.Add("get_user_info");
ClaimActions.MapJsonKey(ClaimTypes.Name, "nickname");
ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
ClaimActions.MapJsonKey(Claims.PictureUrl, "figureurl");
ClaimActions.MapJsonKey(Claims.PictureMediumUrl, "figureurl_1");
ClaimActions.MapJsonKey(Claims.PictureFullUrl, "figureurl_2");
ClaimActions.MapJsonKey(Claims.AvatarUrl, "figureurl_qq_1");
ClaimActions.MapJsonKey(Claims.AvatarFullUrl, "figureurl_qq_2");
}
/// <summary>
/// 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.
/// </summary>
public bool ApplyForUnionId { get; set; }
/// <summary>
/// Gets or sets the URL of the user identification endpoint (a.k.a. the "OpenID endpoint").
/// </summary>
public string UserIdentificationEndpoint { get; set; }
}

View File

@@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
{
public class QQAuthticationcationTokenResponse
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string refresh_token { get; set; }
public string openid { get; set; }
}
public class QQAuthticationcationOpenIdResponse
{
public string client_id { get; set; }
public string openid { get; set; }
}
public class QQAuthticationcationUserInfoResponse
{
// 返回码
public int ret { get; set; }
// 如果ret<0会有相应的错误信息提示
// 返回数据全部用UTF-8编码
public string msg { get; set; }
// 判断是否有数据丢失
// 0或者不返回没有数据丢失可以缓存
// 1有部分数据丢失或错误不要缓存
public int is_lost { get; set; }
// 用户在QQ空间的昵称
public string nickname { get; set; }
// 大小为30x30像素的QQ空间头像URL
public string figureurl { get; set; }
// 大小为50x50像素的QQ空间头像URL
public string figureurl_1 { get; set; }
// 大小为100x100像素的QQ空间头像URL
public string figureurl_2 { get; set; }
// 大小为40x40像素的QQ头像URL
public string figureurl_qq_1 { get; set; }
// 大小为100x100像素的QQ头像URL
// 需要注意不是所有的用户都拥有QQ的100x100的头像但40x40像素则是一定会有
public string figureurl_qq_2 { get; set; }
// 性别。如果获取不到则默认返回"男"
public string gender { get; set; }
// 性别类型。默认返回2
public int gender_type { get; set; }
// 省
public string province { get; set; }
// 市
public string city { get; set; }
// 年
public int year { get; set; }
// 星座
public string constellation { get; set; }
// 标识用户是否为黄钻用户
public int is_yellow_vip { get; set; }
// 黄钻等级
public int yellow_vip_level { get; set; }
// 是否为年费黄钻用户
public int is_yellow_year_vip { get; set; }
}
}

View File

@@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,20 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Yi.Framework.Core;
namespace Yi.Framework.AspNetCore.Authentication.OAuth
{
/// <summary>
/// 本模块轮子来自 AspNet.Security.OAuth.QQ;
/// </summary>
[DependsOn(typeof(YiFrameworkAspNetCoreModule))]
public class YiFrameworkAspNetCoreAuthenticationOAuthModule:AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var service = context.Services;
service.AddHttpClient();
}
}
}

View File

@@ -1,16 +0,0 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Middlewares;
namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder
{
public static class ApiInfoBuilderExtensions
{
public static IApplicationBuilder UseYiApiHandlinge([NotNull] this IApplicationBuilder app)
{
app.UseMiddleware<ApiInfoMiddleware>();
return app;
}
}
}

View File

@@ -1,53 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder
{
public static class SwaggerBuilderExtensons
{
public static IApplicationBuilder UseYiSwagger(this IApplicationBuilder app, params SwaggerModel[] swaggerModels)
{
var mvcOptions = app.ApplicationServices.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().Value;
app.UseSwagger();
app.UseSwaggerUI(c =>
{
foreach (var setting in mvcOptions.ConventionalControllers.ConventionalControllerSettings)
{
c.SwaggerEndpoint($"/swagger/{setting.RemoteServiceName}/swagger.json", setting.RemoteServiceName);
}
if (mvcOptions.ConventionalControllers.ConventionalControllerSettings.Count==0&&swaggerModels.Length == 0)
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Yi.Framework");
}
else
{
foreach (var k in swaggerModels)
{
c.SwaggerEndpoint(k.Url, k.Name);
}
}
});
return app;
}
}
public class SwaggerModel
{
public SwaggerModel(string name)
{
this.Name = name;
this.Url = "/swagger/v1/swagger.json";
}
public SwaggerModel(string url, string name)
{
this.Url = url;
this.Name = name;
}
public string Url { get; set; }
public string Name { get; set; }
}
}

View File

@@ -1,39 +0,0 @@
using System.Diagnostics;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
using Yi.Framework.Core.Extensions;
using static System.Net.WebRequestMethods;
namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Middlewares
{
[DebuggerStepThrough]
public class ApiInfoMiddleware : IMiddleware, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Response.OnStarting([DebuggerStepThrough] () =>
{
if (context.Response.StatusCode == StatusCodes.Status200OK
&& context.Response.Headers["Content-Type"].ToString() == "application/vnd.ms-excel")
{
context.FileAttachmentHandle($"{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.xlsx");
}
if (context.Response.StatusCode == StatusCodes.Status200OK &&
context.Response.Headers["Content-Type"].ToString() == "application/x-zip-compressed")
{
context.FileAttachmentHandle($"{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.zip");
}
return Task.CompletedTask;
});
await next(context);
}
}
}

View File

@@ -1,157 +0,0 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc;
namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
{
public static class SwaggerAddExtensions
{
public static IServiceCollection AddYiSwaggerGen<Program>(this IServiceCollection services, Action<SwaggerGenOptions>? action=null)
{
var serviceProvider = services.BuildServiceProvider();
var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>();
var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
services.AddAbpSwaggerGen(
options =>
{
if (action is not null)
{
action.Invoke(options);
}
// 配置分组,还需要去重,支持重写,如果外部传入后,将以外部为准
foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
{
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
{
options.SwaggerDoc(setting.RemoteServiceName, new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
}
}
// 根据分组名称过滤 API 文档
options.DocInclusionPredicate((docName, apiDesc) =>
{
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var settingOrNull = mvcSettings.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly).FirstOrDefault();
if (settingOrNull is not null)
{
return docName == settingOrNull.RemoteServiceName;
}
}
return false;
});
options.CustomSchemaIds(type => type.FullName);
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
if (basePath is not null)
{
foreach (var item in Directory.GetFiles(basePath, "*.xml"))
{
options.IncludeXmlComments(item, true);
}
}
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
{
Description = "直接输入Token即可",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
options.OperationFilter<AddRequiredHeaderParameter>();
options.SchemaFilter<EnumSchemaFilter>();
}
);
return services;
}
}
/// <summary>
/// Swagger文档枚举字段显示枚举属性和枚举值,以及枚举描述
/// </summary>
public class EnumSchemaFilter : ISchemaFilter
{
/// <summary>
/// 实现接口
/// </summary>
/// <param name="model"></param>
/// <param name="context"></param>
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
model.Enum.Clear();
model.Type = "string";
model.Format = null;
StringBuilder stringBuilder = new StringBuilder();
Enum.GetNames(context.Type)
.ToList()
.ForEach(name =>
{
Enum e = (Enum)Enum.Parse(context.Type, name);
var descrptionOrNull = GetEnumDescription(e);
model.Enum.Add(new OpenApiString(name));
stringBuilder.Append($"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />");
});
model.Description= stringBuilder.ToString();
}
}
private static string? GetEnumDescription(Enum value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : null;
}
}
public class AddRequiredHeaderParameter : IOperationFilter
{
public static string HeaderKey { get; set; } = "__tenant";
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = HeaderKey,
In = ParameterLocation.Header,
Required = false,
AllowEmptyValue = true,
Description="租户id或者租户名称可空为默认租户"
});
}
}
}

View File

@@ -1,73 +0,0 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
namespace Yi.Framework.AspNetCore.Mvc
{
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IConventionalRouteBuilder))]
public class YiConventionalRouteBuilder : ConventionalRouteBuilder
{
public YiConventionalRouteBuilder(IOptions<AbpConventionalControllerOptions> options) : base(options)
{
}
public override string Build(
string rootPath,
string controllerName,
ActionModel action,
string httpMethod,
[CanBeNull] ConventionalControllerSetting configuration)
{
var apiRoutePrefix = GetApiRoutePrefix(action, configuration);
var controllerNameInUrl =
NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration);
var url = $"{rootPath}/{NormalizeControllerNameCase(controllerNameInUrl, configuration)}";
//Add {id} path if needed
var idParameterModel = action.Parameters.FirstOrDefault(p => p.ParameterName == "id");
if (idParameterModel != null)
{
if (TypeHelper.IsPrimitiveExtended(idParameterModel.ParameterType, includeEnums: true))
{
url += "/{id}";
}
else
{
var properties = idParameterModel
.ParameterType
.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
url += "/{" + NormalizeIdPropertyNameCase(property, configuration) + "}";
}
}
}
//Add action name if needed
var actionNameInUrl = NormalizeUrlActionName(rootPath, controllerName, action, httpMethod, configuration);
if (!actionNameInUrl.IsNullOrEmpty())
{
url += $"/{NormalizeActionNameCase(actionNameInUrl, configuration)}";
//Add secondary Id
var secondaryIds = action.Parameters
.Where(p => p.ParameterName.EndsWith("Id", StringComparison.Ordinal)).ToList();
if (secondaryIds.Count == 1)
{
url += $"/{{{NormalizeSecondaryIdNameCase(secondaryIds[0], configuration)}}}";
}
}
return url;
}
}
}

View File

@@ -1,96 +0,0 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
namespace Yi.Framework.AspNetCore.Mvc
{
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IAbpServiceConvention))]
public class YiServiceConvention : AbpServiceConvention
{
public YiServiceConvention(IOptions<AbpAspNetCoreMvcOptions> options, IConventionalRouteBuilder conventionalRouteBuilder) : base(options, conventionalRouteBuilder)
{
}
protected override void ConfigureSelector(string rootPath, string controllerName, ActionModel action, ConventionalControllerSetting? configuration)
{
RemoveEmptySelectors(action.Selectors);
var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(action.ActionMethod);
if (remoteServiceAtt != null && !remoteServiceAtt.IsEnabledFor(action.ActionMethod))
{
return;
}
if (!action.Selectors.Any())
{
AddAbpServiceSelector(rootPath, controllerName, action, configuration);
}
else
{
NormalizeSelectorRoutes(rootPath, controllerName, action, configuration);
}
}
protected override void AddAbpServiceSelector(string rootPath, string controllerName, ActionModel action, ConventionalControllerSetting? configuration)
{
base.AddAbpServiceSelector(rootPath, controllerName, action, configuration);
}
protected override void NormalizeSelectorRoutes(string rootPath, string controllerName, ActionModel action, ConventionalControllerSetting? configuration)
{
foreach (var selector in action.Selectors)
{
var httpMethod = selector.ActionConstraints
.OfType<HttpMethodActionConstraint>()
.FirstOrDefault()?
.HttpMethods?
.FirstOrDefault();
if (httpMethod == null)
{
httpMethod = SelectHttpMethod(action, configuration);
}
if (selector.AttributeRouteModel == null)
{
selector.AttributeRouteModel = CreateAbpServiceAttributeRouteModel(rootPath, controllerName, action, httpMethod, configuration);
}
else
{
var template = selector.AttributeRouteModel.Template;
if (!template.StartsWith("/"))
{
var route = $"{rootPath}/{template}";
selector.AttributeRouteModel.Template = route;
}
}
if (!selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Any())
{
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { httpMethod }));
}
}
}
}
}

View File

@@ -1,37 +0,0 @@
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.WebClientInfo;
namespace Yi.Framework.AspNetCore;
public class RealIpHttpContextWebClientInfoProvider : HttpContextWebClientInfoProvider
{
public RealIpHttpContextWebClientInfoProvider(ILogger<HttpContextWebClientInfoProvider> logger,
IHttpContextAccessor httpContextAccessor) : base(logger, httpContextAccessor)
{
}
protected override string? GetClientIpAddress()
{
try
{
var httpContext = HttpContextAccessor.HttpContext;
var headers = httpContext?.Request?.Headers;
if (headers != null && headers.ContainsKey("X-Forwarded-For"))
{
httpContext.Connection.RemoteIpAddress =
IPAddress.Parse(headers["X-Forwarded-For"].FirstOrDefault());
}
return httpContext?.Connection?.RemoteIpAddress?.ToString();
}
catch (Exception ex)
{
Logger.LogException(ex, LogLevel.Warning);
return null;
}
}
}

View File

@@ -1,49 +0,0 @@
namespace Yi.Framework.AspNetCore
{
[Serializable]
public class RemoteServiceSuccessInfo
{
/// <summary>
/// Creates a new instance of <see cref="RemoteServiceSuccessInfo"/>.
/// </summary>
public RemoteServiceSuccessInfo()
{
}
/// <summary>
/// Creates a new instance of <see cref="RemoteServiceSuccessInfo"/>.
/// </summary>
/// <param name="code">Error code</param>
/// <param name="details">Error details</param>
/// <param name="message">Error message</param>
/// <param name="data">Error data</param>
public RemoteServiceSuccessInfo(string message, string? details = null, string? code = null, object? data = null)
{
Message = message;
Details = details;
Code = code;
Data = data;
}
/// <summary>
/// code.
/// </summary>
public string? Code { get; set; }
/// <summary>
/// message.
/// </summary>
public string? Message { get; set; }
/// <summary>
/// details.
/// </summary>
public string? Details { get; set; }
/// <summary>
/// data.
/// </summary>
public object? Data { get; set; }
}
}

View File

@@ -1,46 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 异常元数据
/// </summary>
public sealed class ExceptionMetadata
{
/// <summary>
/// 状态码
/// </summary>
public int StatusCode { get; internal set; }
/// <summary>
/// 错误码
/// </summary>
public object ErrorCode { get; internal set; }
/// <summary>
/// 错误码(没被复写过的 ErrorCode
/// </summary>
public object OriginErrorCode { get; internal set; }
/// <summary>
/// 错误对象(信息)
/// </summary>
public object Errors { get; internal set; }
/// <summary>
/// 额外数据
/// </summary>
public object Data { get; internal set; }
}

View File

@@ -1,106 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Core.Extensions;
namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
/// <summary>
/// 友好异常拦截器
/// </summary>
public sealed class FriendlyExceptionFilter : IAsyncExceptionFilter
{
/// <summary>
/// 异常拦截
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnExceptionAsync(ExceptionContext context)
{
// 排除 WebSocket 请求处理
if (context.HttpContext.IsWebSocketRequest()) return;
// 如果异常在其他地方被标记了处理,那么这里不再处理
if (context.ExceptionHandled) return;
// 解析异常信息
var exceptionMetadata = GetExceptionMetadata(context);
IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
// 执行规范化异常处理
context.Result = unifyResult.OnException(context, exceptionMetadata);
// 创建日志记录器
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<FriendlyExceptionFilter>>();
// 记录拦截日常
logger.LogError(context.Exception, context.Exception.Message);
}
/// <summary>
/// 获取异常元数据
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static ExceptionMetadata GetExceptionMetadata(ActionContext context)
{
object errorCode = default;
object originErrorCode = default;
object errors = default;
object data = default;
var statusCode = StatusCodes.Status500InternalServerError;
var isValidationException = false; // 判断是否是验证异常
var isFriendlyException = false;
// 判断是否是 ExceptionContext 或者 ActionExecutedContext
var exception = context is ExceptionContext exContext
? exContext.Exception
: (
context is ActionExecutedContext edContext
? edContext.Exception
: default
);
// 判断是否是友好异常
if (exception is UserFriendlyException friendlyException)
{
int statusCode2 = 500;
int.TryParse(friendlyException.Code, out statusCode2);
isFriendlyException = true;
errorCode = friendlyException.Code;
originErrorCode = friendlyException.Code;
statusCode = statusCode2==0?403:statusCode2;
isValidationException = false;
errors = friendlyException.Message;
data = friendlyException.Data;
}
return new ExceptionMetadata
{
StatusCode = statusCode,
ErrorCode = errorCode,
OriginErrorCode = originErrorCode,
Errors = errors,
Data = data
};
}
}

View File

@@ -1,276 +0,0 @@
using System.Collections;
using System.Reflection;
using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Core.Extensions;
namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
/// <summary>
/// 规范化结构(请求成功)过滤器
/// </summary>
public class SucceededUnifyResultFilter : IAsyncActionFilter, IOrderedFilter
{
/// <summary>
/// 过滤器排序
/// </summary>
private const int FilterOrder = 8888;
/// <summary>
/// 排序属性
/// </summary>
public int Order => FilterOrder;
/// <summary>
/// 处理规范化结果
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 执行 Action 并获取结果
var actionExecutedContext = await next();
// 排除 WebSocket 请求处理
if (actionExecutedContext.HttpContext.IsWebSocketRequest()) return;
// 处理已经含有状态码结果的 Result
if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult &&
statusCodeResult.StatusCode != null)
{
// 小于 200 或者 大于 299 都不是成功值,直接跳过
if (statusCodeResult.StatusCode.Value < 200 || statusCodeResult.StatusCode.Value > 299)
{
// 处理规范化结果
if (!CheckStatusCodeNonUnify(context.HttpContext, out var unifyRes))
{
var httpContext = context.HttpContext;
var statusCode = statusCodeResult.StatusCode.Value;
// 解决刷新 Token 时间和 Token 时间相近问题
if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
&& httpContext.Response.Headers.ContainsKey("access-token")
&& httpContext.Response.Headers.ContainsKey("x-access-token"))
{
httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
}
// 如果 Response 已经完成输出,则禁止写入
if (httpContext.Response.HasStarted) return;
await unifyRes.OnResponseStatusCodes(httpContext, statusCode,
httpContext.RequestServices.GetService<IOptions<UnifyResultSettingsOptions>>()?.Value);
}
return;
}
}
// 如果出现异常,则不会进入该过滤器
if (actionExecutedContext.Exception != null) return;
// 获取控制器信息
var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
// 判断是否支持 MVC 规范化处理,检测配置而已
// if (!UnifyContext.CheckSupportMvcController(context.HttpContext, actionDescriptor, out _)) return;
// 判断是否跳过规范化处理检测NonUnifyAttribute而已
if (CheckSucceededNonUnify(actionDescriptor.MethodInfo))
{
return;
}
IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
// 处理 BadRequestObjectResult 类型规范化处理
if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
{
// 解析验证消息
var validationMetadata = GetValidationMetadata(badRequestObjectResult.Value);
var result = unifyResult.OnValidateFailed(context, validationMetadata);
if (result != null) actionExecutedContext.Result = result;
}
else
{
IActionResult result = default;
// 检查是否是有效的结果(可进行规范化的结果)
if (CheckVaildResult(actionExecutedContext.Result, out var data))
{
result = unifyResult.OnSucceeded(actionExecutedContext, data);
}
// 如果是不能规范化的结果类型,则跳过
if (result == null) return;
actionExecutedContext.Result = result;
}
}
/// <summary>
/// 获取验证错误信息
/// </summary>
/// <param name="errors"></param>
/// <returns></returns>
private static ValidationMetadata GetValidationMetadata(object errors)
{
ModelStateDictionary _modelState = null;
object validationResults = null;
(string message, string firstErrorMessage, string firstErrorProperty) = (default, default, default);
// 判断是否是集合类型
if (errors is IEnumerable && errors is not string)
{
// 如果是模型验证字典类型
if (errors is ModelStateDictionary modelState)
{
_modelState = modelState;
// 将验证错误信息转换成字典并序列化成 Json
validationResults = modelState.Where(u => modelState[u.Key].ValidationState == ModelValidationState.Invalid)
.ToDictionary(u => u.Key, u => modelState[u.Key].Errors.Select(c => c.ErrorMessage).ToArray());
}
// 如果是 ValidationProblemDetails 特殊类型
else if (errors is ValidationProblemDetails validation)
{
validationResults = validation.Errors
.ToDictionary(u => u.Key, u => u.Value.ToArray());
}
// 如果是字典类型
else if (errors is Dictionary<string, string[]> dicResults)
{
validationResults = dicResults;
}
message = JsonSerializer.Serialize(validationResults, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
});
firstErrorMessage = (validationResults as Dictionary<string, string[]>).First().Value[0];
firstErrorProperty = (validationResults as Dictionary<string, string[]>).First().Key;
}
// 其他类型
else
{
validationResults = firstErrorMessage = message = errors?.ToString();
}
return new ValidationMetadata
{
ValidationResult = validationResults,
Message = message,
ModelState = _modelState,
FirstErrorProperty = firstErrorProperty,
FirstErrorMessage = firstErrorMessage
};
}
/// <summary>
/// 检查是否是有效的结果(可进行规范化的结果)
/// </summary>
/// <param name="result"></param>
/// <param name="data"></param>
/// <returns></returns>
private bool CheckVaildResult(IActionResult result, out object data)
{
data = default;
// 排除以下结果,跳过规范化处理
var isDataResult = result switch
{
ViewResult => false,
PartialViewResult => false,
FileResult => false,
ChallengeResult => false,
SignInResult => false,
SignOutResult => false,
RedirectToPageResult => false,
RedirectToRouteResult => false,
RedirectResult => false,
RedirectToActionResult => false,
LocalRedirectResult => false,
ForbidResult => false,
ViewComponentResult => false,
PageResult => false,
NotFoundResult => false,
NotFoundObjectResult => false,
_ => true,
};
// 目前支持返回值 ActionResult
if (isDataResult) data = result switch
{
// 处理内容结果
ContentResult content => content.Content,
// 处理对象结果
ObjectResult obj => obj.Value,
// 处理 JSON 对象
JsonResult json => json.Value,
_ => null,
};
return isDataResult;
}
/// <summary>
/// 检查短路状态码(>=400是否进行规范化处理
/// </summary>
/// <param name="context"></param>
/// <param name="unifyResult"></param>
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
internal static bool CheckStatusCodeNonUnify(HttpContext context, out IUnifyResultProvider unifyResult)
{
// 获取终点路由特性
var endpointFeature = context.Features.Get<IEndpointFeature>();
if (endpointFeature == null) return (unifyResult = null) == null;
// 判断是否跳过规范化处理
var isSkip = context.GetEndpoint()?.Metadata?.GetMetadata<NonUnifyAttribute>()!= null
|| endpointFeature?.Endpoint?.Metadata?.GetMetadata<NonUnifyAttribute>() != null
|| context.Request.Headers["accept"].ToString().Contains("odata.metadata=", StringComparison.OrdinalIgnoreCase)
|| context.Request.Headers["accept"].ToString().Contains("odata.streaming=", StringComparison.OrdinalIgnoreCase);
if (isSkip == true) unifyResult = null;
else
{
unifyResult = context.RequestServices.GetRequiredService<IUnifyResultProvider>();
}
return unifyResult == null || isSkip;
}
/// <summary>
/// 检查请求成功是否进行规范化处理
/// </summary>
/// <param name="method"></param>
/// <param name="isWebRequest"></param>
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
private bool CheckSucceededNonUnify(MethodInfo method, bool isWebRequest = true)
{
// 判断是否跳过规范化处理
var isSkip = method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType) || typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType))
|| method.ReflectedType.IsDefined(typeof(NonUnifyAttribute), true)
|| method.DeclaringType.Assembly.GetName().Name.StartsWith("Microsoft.AspNetCore.OData");
if (!isWebRequest)
{
return isSkip;
}
return isSkip;
}
}

View File

@@ -1,58 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化结果提供器
/// </summary>
public interface IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata);
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
IActionResult OnSucceeded(ActionExecutedContext context, object data);
/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata);
/// <summary>
/// 拦截返回状态码
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = default);
}

View File

@@ -1,23 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 禁止规范化处理
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class NonUnifyAttribute : Attribute
{
}

View File

@@ -1,136 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Volo.Abp.DependencyInjection;
namespace Yi.Framework.AspNetCore.UnifyResult.Providers;
/// <summary>
/// RESTful 风格返回值
/// </summary>
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IUnifyResultProvider))]
public class RESTfulResultProvider : IUnifyResultProvider,ITransientDependency
{
/// <summary>
/// 设置响应状态码
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
public static void SetResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
if (unifyResultSettings == null) return;
// 篡改响应状态码
if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0)
{
var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0)
{
context.Response.StatusCode = adaptStatusCode[1];
return;
}
}
// 如果为 null则所有请求错误的状态码设置为 200
if (unifyResultSettings.Return200StatusCodes == null) context.Response.StatusCode = 200;
// 否则只有里面的才设置为 200
else if (unifyResultSettings.Return200StatusCodes.Contains(statusCode)) context.Response.StatusCode = 200;
else { }
}
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
}
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}
/// <summary>
/// 验证失败/业务异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
}
/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
SetResponseStatusCodes(context, statusCode, unifyResultSettings);
switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized"));
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden"));
break;
default: break;
}
}
/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
public static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RESTfulResult<object>
{
StatusCode = statusCode,
Succeeded = succeeded,
Data = data,
Errors = errors,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
}
}

View File

@@ -1,52 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// RESTful 风格结果集
/// </summary>
/// <typeparam name="T"></typeparam>
public class RESTfulResult<T>
{
/// <summary>
/// 状态码
/// </summary>
public int? StatusCode { get; set; }
/// <summary>
/// 数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 执行成功
/// </summary>
public bool Succeeded { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public object Errors { get; set; }
/// <summary>
/// 附加数据
/// </summary>
public object Extras { get; set; }
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
}

View File

@@ -1,29 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Yi.Framework.AspNetCore.UnifyResult.Fiters;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化接口
/// 由于太多人反应想兼容一套类似furion的返回情况200状态码包一层更符合国内习惯既然如此不如直接搬过来
/// </summary>
public static class UnifyResultExtensions
{
public static IServiceCollection AddFurionUnifyResultApi(this IServiceCollection services)
{
//成功规范接口
services.AddTransient<SucceededUnifyResultFilter>();
//异常规范接口
services.AddTransient<FriendlyExceptionFilter>();
services.AddMvc(options =>
{
options.Filters.AddService<SucceededUnifyResultFilter>(99);
options.Filters.AddService<FriendlyExceptionFilter>(100);
options.Filters.RemoveAll(x => (x as ServiceFilterAttribute)?.ServiceType == typeof(AbpExceptionFilter));
});
return services;
}
}

View File

@@ -1,50 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.Extensions.Configuration;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化配置选项
/// </summary>
public sealed class UnifyResultSettingsOptions
{
/// <summary>
/// 设置返回 200 状态码列表
/// <para>默认401403如果设置为 null则标识所有状态码都返回 200 </para>
/// </summary>
public int[] Return200StatusCodes { get; set; }
/// <summary>
/// 适配篡改Http 状态码(只支持短路状态码,比如 401403500 等)
/// </summary>
public int[][] AdaptStatusCodes { get; set; }
/// <summary>
/// 是否支持 MVC 控制台规范化处理
/// </summary>
public bool? SupportMvcController { get; set; }
/// <summary>
/// 选项后期配置
/// </summary>
/// <param name="options"></param>
/// <param name="configuration"></param>
public void PostConfigure(UnifyResultSettingsOptions options, IConfiguration configuration)
{
options.Return200StatusCodes ??= new[] { 401, 403 };
options.SupportMvcController ??= false;
}
}

View File

@@ -1,69 +0,0 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 验证信息元数据
/// </summary>
public sealed class ValidationMetadata
{
/// <summary>
/// 验证结果
/// </summary>
/// <remarks>返回字典或字符串类型</remarks>
public object ValidationResult { get; internal set; }
/// <summary>
/// 异常消息
/// </summary>
public string Message { get; internal set; }
/// <summary>
/// 验证状态
/// </summary>
public ModelStateDictionary ModelState { get; internal set; }
/// <summary>
/// 错误码
/// </summary>
public object ErrorCode { get; internal set; }
/// <summary>
/// 错误码(没被复写过的 ErrorCode
/// </summary>
public object OriginErrorCode { get; internal set; }
/// <summary>
/// 状态码
/// </summary>
public int? StatusCode { get; internal set; }
/// <summary>
/// 首个错误属性
/// </summary>
public string FirstErrorProperty { get; internal set; }
/// <summary>
/// 首个错误消息
/// </summary>
public string FirstErrorMessage { get; internal set; }
/// <summary>
/// 额外数据
/// </summary>
public object Data { get; internal set; }
}

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<Compile Remove="Cors\**" />
<EmbeddedResource Remove="Cors\**" />
<None Remove="Cors\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Json" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Swashbuckle" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,33 +0,0 @@
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Linq;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
using Yi.Framework.AspNetCore.Mvc;
using Yi.Framework.Core;
namespace Yi.Framework.AspNetCore
{
[DependsOn(typeof(YiFrameworkCoreModule)
)]
public class YiFrameworkAspNetCoreModule : AbpModule
{
public override void PostConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.Replace(new ServiceDescriptor(typeof(IWebClientInfoProvider),
typeof(RealIpHttpContextWebClientInfoProvider), ServiceLifetime.Transient));
}
}
}

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" Version="$(AbpVersion)" />
</ItemGroup>
</Project>

View File

@@ -1,27 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.BackgroundWorkers.Hangfire;
namespace Yi.Framework.BackgroundWorkers.Hangfire;
[DependsOn(typeof(AbpBackgroundWorkersHangfireModule))]
public class YiFrameworkBackgroundWorkersHangfireModule:AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddConventionalRegistrar(new YiHangfireConventionalRegistrar());
}
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
//定时任务自动注入Abp默认只有在Quartz才实现
var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
var works = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
foreach (var work in works)
{
await backgroundWorkerManager.AddAsync(work);
}
}
}

View File

@@ -1,20 +0,0 @@
using Volo.Abp.BackgroundWorkers.Hangfire;
using Volo.Abp.DependencyInjection;
namespace Yi.Framework.BackgroundWorkers.Hangfire;
public class YiHangfireConventionalRegistrar : DefaultConventionalRegistrar
{
protected override bool IsConventionalRegistrationDisabled(Type type)
{
return !typeof(IHangfireBackgroundWorker).IsAssignableFrom(type) || base.IsConventionalRegistrationDisabled(type);
}
protected override List<Type> GetExposedServiceTypes(Type type)
{
return new List<Type>()
{
typeof(IHangfireBackgroundWorker)
};
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FreeRedis;
namespace Yi.Framework.Caching.FreeRedis
{
/// <summary>
/// 便于转到定义
/// </summary>
public class FreeSqlOptions: ConnectionStringBuilder
{
}
}

View File

@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FreeRedis" Version="1.2.14" />
<PackageReference Include="FreeRedis.DistributedCache" Version="1.2.5" />
<PackageReference Include="Volo.Abp.Caching" Version="$(AbpVersion)" />
</ItemGroup>
</Project>

View File

@@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Yi.Framework.Caching.FreeRedis
{
[Dependency(ReplaceServices =true)]
public class YiDistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency
{
protected ICurrentTenant CurrentTenant { get; }
protected AbpDistributedCacheOptions DistributedCacheOptions { get; }
public YiDistributedCacheKeyNormalizer(
ICurrentTenant currentTenant,
IOptions<AbpDistributedCacheOptions> distributedCacheOptions)
{
CurrentTenant = currentTenant;
DistributedCacheOptions = distributedCacheOptions.Value;
}
public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args)
{
var normalizedKey = $"{DistributedCacheOptions.KeyPrefix}{args.Key}";
//if (!args.IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
//{
// normalizedKey = $"t:{CurrentTenant.Id.Value},{normalizedKey}";
//}
return normalizedKey;
}
}
}

View File

@@ -1,32 +0,0 @@
using FreeRedis;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Caching;
namespace Yi.Framework.Caching.FreeRedis
{
/// <summary>
/// 此模块得益于FreeRedis作者支持IDistributedCache使用湿滑
/// </summary>
[DependsOn(typeof(AbpCachingModule))]
public class YiFrameworkCachingFreeRedisModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var redisEnabled = configuration["Redis:IsEnabled"];
if (redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled))
{
var redisConfiguration = configuration["Redis:Configuration"];
RedisClient redisClient = new RedisClient(redisConfiguration);
context.Services.AddSingleton<IRedisClient>(redisClient);
context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache>(new
DistributedCache(redisClient)));
}
}
}
}

View File

@@ -1,27 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Yi.Framework.Core.Json;
public class DatetimeJsonConverter : JsonConverter<DateTime>
{
private string _format;
public DatetimeJsonConverter(string format="yyyy-MM-dd HH:mm:ss")
{
_format = format;
}
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(reader.TokenType==JsonTokenType.String)
{
if (DateTime.TryParse(reader.GetString(), out DateTime dateTime)) return dateTime;
}
return reader.GetDateTime();
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}

View File

@@ -1,59 +0,0 @@
using System.Diagnostics;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
namespace Yi.Framework.Core.Modularity;
[Dependency(ReplaceServices =true)]
public class YiModuleManager : ModuleManager, IModuleManager, ISingletonDependency
{
private readonly IModuleContainer _moduleContainer;
private readonly IEnumerable<IModuleLifecycleContributor> _lifecycleContributors;
private readonly ILogger<YiModuleManager> _logger;
public YiModuleManager(IModuleContainer moduleContainer, ILogger<YiModuleManager> logger, IOptions<AbpModuleLifecycleOptions> options, IServiceProvider serviceProvider) : base(moduleContainer, logger, options, serviceProvider)
{
_moduleContainer = moduleContainer;
_logger = logger;
_lifecycleContributors = options.Value.Contributors.Select(serviceProvider.GetRequiredService).Cast<IModuleLifecycleContributor>().ToArray();
}
public override async Task InitializeModulesAsync(ApplicationInitializationContext context)
{
_logger.LogDebug("==========模块Initialize初始化统计-跳过0ms模块==========");
var total = 0;
var watch =new Stopwatch();
long totalTime = 0;
foreach (var contributor in _lifecycleContributors)
{
foreach (var module in _moduleContainer.Modules)
{
try
{
watch.Restart();
await contributor.InitializeAsync(context, module.Instance);
watch.Stop();
totalTime += watch.ElapsedMilliseconds;
total++;
if (watch.ElapsedMilliseconds > 1)
{
_logger.LogDebug($"耗时-{watch.ElapsedMilliseconds}ms,已加载模块-{module.Assembly.GetName().Name}");
}
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex);
}
}
}
_logger.LogInformation($"==========【{total}】个模块初始化执行完毕,总耗时【{totalTime}ms】==========");
}
}

View File

@@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Volo.Abp.Core" Version="$(AbpVersion)" />
</ItemGroup>
</Project>

View File

@@ -1,9 +0,0 @@
using Volo.Abp.Modularity;
namespace Yi.Framework.Core
{
public class YiFrameworkCoreModule:AbpModule
{
}
}

View File

@@ -1,10 +0,0 @@
using Volo.Abp;
using Volo.Abp.Application.Services;
namespace Yi.Framework.Ddd.Application.Contracts
{
public interface IDeletesAppService<in TKey> : IDeleteAppService< TKey> , IApplicationService, IRemoteService
{
Task DeleteAsync(IEnumerable<TKey> ids);
}
}

View File

@@ -1,10 +0,0 @@
using Volo.Abp.Application.Dtos;
namespace Yi.Framework.Ddd.Application.Contracts
{
public interface IPageTimeResultRequestDto : IPagedAndSortedResultRequest
{
DateTime? StartTime { get; set; }
DateTime? EndTime { get; set; }
}
}

View File

@@ -1,8 +0,0 @@
using Volo.Abp.Application.Dtos;
namespace Yi.Framework.Ddd.Application.Contracts
{
public interface IPagedAllResultRequestDto : IPageTimeResultRequestDto, IPagedAndSortedResultRequest
{
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace Yi.Framework.Ddd.Application.Contracts
{
public interface IYiCrudAppService<TEntityDto, in TKey> : ICrudAppService<TEntityDto, TKey>
{
}
public interface IYiCrudAppService<TEntityDto, in TKey, in TGetListInput> : ICrudAppService<TEntityDto, TKey, TGetListInput>
{
}
public interface IYiCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput> : ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput>
{
}
public interface IYiCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> : ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
{
}
public interface IYiCrudAppService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> : ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>, IDeletesAppService<TKey>
{
}
}

View File

@@ -1,51 +0,0 @@
using Volo.Abp.Application.Dtos;
namespace Yi.Framework.Ddd.Application.Contracts
{
public class PagedAllResultRequestDto : PagedAndSortedResultRequestDto, IPagedAllResultRequestDto
{
/// <summary>
/// 查询开始时间条件
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// 查询结束时间条件
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 排序列名,字段名对应前端
/// </summary>
public string? OrderByColumn { get; set; }
/// <summary>
/// 是否顺序,字段名对应前端
/// </summary>
public string? IsAsc { get; set; }
/// <summary>
/// 是否顺序
/// </summary>
public bool CanAsc => IsAsc?.ToLower() == "ascending" ? true : false;
private string _sorting;
//排序引用
public new string? Sorting
{
get
{
if (!OrderByColumn.IsNullOrWhiteSpace())
{
return $"{OrderByColumn} {(CanAsc ? "ASC" : "DESC")}";
}
else
{
return _sorting;
}
}
set => _sorting = value;
}
}
}

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,10 +0,0 @@
using Volo.Abp.Application;
using Volo.Abp.Modularity;
namespace Yi.Framework.Ddd.Application.Contracts
{
[DependsOn(typeof(AbpDddApplicationContractsModule))]
public class YiFrameworkDddApplicationContractsModule : AbpModule
{
}
}

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<PackageReference Include="MiniExcel" Version="1.31.3" />
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.Ddd.Application.Contracts\Yi.Framework.Ddd.Application.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,119 +0,0 @@
using Volo.Abp.Application.Dtos;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
namespace Yi.Framework.Ddd.Application
{
public abstract class YiCacheCrudAppService<TEntity, TEntityDto, TKey> : YiCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCacheCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCacheCrudAppService<TEntity, TEntityDto, TKey, TGetListInput>
: YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCacheCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCacheCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput>
: YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCacheCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCacheCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: YiCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCacheCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCacheCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: YiCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected IDistributedCache<TEntity> Cache => LazyServiceProvider.LazyGetRequiredService<IDistributedCache<TEntity>>();
protected string GetCacheKey(TKey id) => typeof(TEntity).Name + ":" + CurrentTenant.Id ?? Guid.Empty + ":" + id.ToString();
protected YiCacheCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
public override async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
var output = await base.UpdateAsync(id, input);
await Cache.RemoveAsync(GetCacheKey(id));
return output;
}
public override async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
//两种方式:
//1全表缓存使用缓存直接查询
//2非全部缓存查询到的数据直接添加到缓存
//判断是否该实体为全表缓存
throw new NotImplementedException();
//IDistributedCache 有局限性,条件查询无法进行缓存了
//if (true)
//{
// return await GetListByCacheAsync(input);
//}
//else
//{
// return await GetListByDbAsync(input);
//}
}
protected virtual async Task<PagedResultDto<TGetListOutputDto>> GetListByDbAsync(TGetListInput input)
{
//如果不是全表缓存,可以走这个啦
throw new NotImplementedException();
}
protected virtual async Task<PagedResultDto<TGetListOutputDto>> GetListByCacheAsync(TGetListInput input)
{
//如果是全表缓存,可以走这个啦
throw new NotImplementedException();
}
protected override async Task<TEntity> GetEntityByIdAsync(TKey id)
{
var output = await Cache.GetOrAddAsync(GetCacheKey(id), async () => await base.GetEntityByIdAsync(id));
return output!;
}
public override async Task DeleteAsync(IEnumerable<TKey> id)
{
await base.DeleteAsync(id);
foreach (var itemId in id)
{
await Cache.RemoveAsync(GetCacheKey(itemId));
}
}
}
}

View File

@@ -1,190 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
namespace Yi.Framework.Ddd.Application
{
public abstract class
YiCrudAppService<TEntity, TEntityDto, TKey> : YiCrudAppService<TEntity, TEntityDto, TKey,
PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput>
: YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput>
: YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: YiCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
}
public abstract class YiCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput,
TUpdateInput>
: CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
public override async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
await CheckUpdatePolicyAsync();
var entity = await GetEntityByIdAsync(id);
await CheckUpdateInputDtoAsync(entity,input);
await MapToEntityAsync(input, entity);
await Repository.UpdateAsync(entity, autoSave: true);
return await MapToGetOutputDtoAsync(entity);
}
protected virtual Task CheckUpdateInputDtoAsync(TEntity entity,TUpdateInput input)
{
return Task.CompletedTask;
}
public override async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync();
await CheckCreateInputDtoAsync(input);
var entity = await MapToEntityAsync(input);
TryToSetTenantId(entity);
await Repository.InsertAsync(entity, autoSave: true);
return await MapToGetOutputDtoAsync(entity);
}
protected virtual Task CheckCreateInputDtoAsync(TCreateInput input)
{
return Task.CompletedTask;
}
/// <summary>
/// 多查
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
List<TEntity>? entites = null;
//区分多查还是批量查
if (input is IPagedResultRequest pagedInput)
{
entites = await Repository.GetPagedListAsync(pagedInput.SkipCount, pagedInput.MaxResultCount,
string.Empty);
}
else
{
entites = await Repository.GetListAsync();
}
var total = await Repository.GetCountAsync();
var output = await MapToGetListOutputDtosAsync(entites);
return new PagedResultDto<TGetListOutputDto>(total, output);
//throw new NotImplementedException($"【{typeof(TEntity)}】实体的CrudAppService查询为具体业务通用查询几乎无实际场景请重写实现");
}
/// <summary>
/// 多删
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[RemoteService(isEnabled: true)]
public virtual async Task DeleteAsync(IEnumerable<TKey> id)
{
await Repository.DeleteManyAsync(id);
}
/// <summary>
/// 偷梁换柱
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[RemoteService(isEnabled: false)]
public override Task DeleteAsync(TKey id)
{
return base.DeleteAsync(id);
}
/// <summary>
/// 导出excel
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public virtual async Task<IActionResult> GetExportExcelAsync(TGetListInput input)
{
if (input is IPagedResultRequest paged)
{
paged.SkipCount = 0;
paged.MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount;
}
var output = await this.GetListAsync(input);
var dirPath = $"/wwwroot/temp";
var fileName = $"{typeof(TEntity).Name}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}_{Guid.NewGuid()}";
var filePath = $"{dirPath}/{fileName}.xlsx";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
MiniExcel.SaveAs(filePath, output.Items);
return new PhysicalFileResult(filePath, "application/vnd.ms-excel");
}
/// <summary>
/// 导入excle
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public virtual async Task PostImportExcelAsync(List<TCreateInput> input)
{
var entities = input.Select(x => MapToEntity(x)).ToList();
//安全起见,该接口需要自己实现
throw new NotImplementedException();
//await Repository.DeleteManyAsync(entities.Select(x => x.Id));
//await Repository.InsertManyAsync(entities);
}
}
}

View File

@@ -1,20 +0,0 @@
using Volo.Abp;
using Volo.Abp.Application;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity;
using Yi.Framework.Ddd.Application.Contracts;
namespace Yi.Framework.Ddd.Application
{
[DependsOn(typeof(AbpDddApplicationModule),
typeof(YiFrameworkDddApplicationContractsModule))]
public class YiFrameworkDddApplicationModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
//分页限制
LimitedResultRequestDto.DefaultMaxResultCount = 10;
LimitedResultRequestDto.MaxMaxResultCount = 10000;
}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Mapster;
using Volo.Abp.ObjectMapping;
namespace Yi.Framework.Mapster
{
public class MapsterAutoObjectMappingProvider : IAutoObjectMappingProvider
{
public TDestination Map<TSource, TDestination>(object source)
{
var sss = typeof(TDestination).Name;
return source.Adapt<TDestination>();
}
public TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
{
return source.Adapt<TSource, TDestination>(destination);
}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.ObjectMapping;
namespace Yi.Framework.Mapster
{
public class MapsterObjectMapper : IObjectMapper
{
public IAutoObjectMappingProvider AutoObjectMappingProvider => throw new NotImplementedException();
public TDestination Map<TSource, TDestination>(TSource source)
{
throw new NotImplementedException();
}
public TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Volo.Abp.ObjectMapping" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,21 +0,0 @@
using MapsterMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectMapping;
using Yi.Framework.Core;
namespace Yi.Framework.Mapster
{
[DependsOn(typeof(YiFrameworkCoreModule),
typeof(AbpObjectMappingModule)
)]
public class YiFrameworkMapsterModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<IAutoObjectMappingProvider, MapsterAutoObjectMappingProvider>();
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
[AttributeUsage(AttributeTargets.Class)]
public class DefaultTenantTableAttribute : Attribute
{
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using SqlSugar;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
public interface ISqlSugarDbConnectionCreator
{
DbConnOptions Options { get; }
Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
Action<object, DataAfterModel> DataExecuted { get; set; }
Action<object, DataFilterModel> DataExecuting { get; set; }
Action<string, SugarParameter[]> OnLogExecuting { get; set; }
Action<string, SugarParameter[]> OnLogExecuted { get; set; }
Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
ConnectionConfig Build(Action<ConnectionConfig>? action = null);
void SetDbAop(ISqlSugarClient currentDb);
}
}

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SqlSugar;
using Volo.Abp.DependencyInjection;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
public interface ISqlSugarDbContext
{
// IAbpLazyServiceProvider LazyServiceProvider { get; set; }
ISqlSugarClient SqlSugarClient { get; }
DbConnOptions Options { get; }
/// <summary>
/// 数据库备份
/// </summary>
void BackupDataBase();
void SetSqlSugarClient(ISqlSugarClient sqlSugarClient);
}
}

View File

@@ -1,88 +0,0 @@
using System.Linq.Expressions;
using SqlSugar;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
public interface ISqlSugarRepository<TEntity>:IRepository<TEntity> where TEntity : class, IEntity,new ()
{
ISqlSugarClient _Db { get; }
ISugarQueryable<TEntity> _DbQueryable { get; }
Task<ISqlSugarClient> GetDbContextAsync();
Task<IDeleteable<TEntity>> AsDeleteable();
Task<IInsertable<TEntity>> AsInsertable(List<TEntity> insertObjs);
Task<IInsertable<TEntity>> AsInsertable(TEntity insertObj);
Task<IInsertable<TEntity>> AsInsertable(TEntity[] insertObjs);
Task<ISugarQueryable<TEntity>> AsQueryable();
Task<ISqlSugarClient> AsSugarClient();
Task<ITenant> AsTenant();
Task<IUpdateable<TEntity>> AsUpdateable(List<TEntity> updateObjs);
Task<IUpdateable<TEntity>> AsUpdateable(TEntity updateObj);
Task<IUpdateable<TEntity>> AsUpdateable();
Task<IUpdateable<TEntity>> AsUpdateable(TEntity[] updateObjs);
#region
//单查
Task<TEntity> GetByIdAsync(dynamic id);
Task<TEntity> GetSingleAsync(Expression<Func<TEntity, bool>> whereExpression);
Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression);
Task<bool> IsAnyAsync(Expression<Func<TEntity, bool>> whereExpression);
Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression);
#endregion
#region
//多查
Task<List<TEntity>> GetListAsync();
Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> whereExpression);
#endregion
#region
//分页查
Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression, int pageNum, int pageSize);
Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression, int pageNum, int pageSize, Expression<Func<TEntity, object>>? orderByExpression = null, OrderByType orderByType = OrderByType.Asc);
#endregion
#region
//插入
Task<bool> InsertAsync(TEntity insertObj);
Task<bool> InsertOrUpdateAsync(TEntity data);
Task<bool> InsertOrUpdateAsync(List<TEntity> datas);
Task<int> InsertReturnIdentityAsync(TEntity insertObj);
Task<long> InsertReturnBigIdentityAsync(TEntity insertObj);
Task<long> InsertReturnSnowflakeIdAsync(TEntity insertObj);
Task<TEntity> InsertReturnEntityAsync(TEntity insertObj);
Task<bool> InsertRangeAsync(List<TEntity> insertObjs);
#endregion
#region
//更新
Task<bool> UpdateAsync(TEntity updateObj);
Task<bool> UpdateRangeAsync(List<TEntity> updateObjs);
Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression);
#endregion
#region
//删除
Task<bool> DeleteAsync(TEntity deleteObj);
Task<bool> DeleteAsync(List<TEntity> deleteObjs);
Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression);
Task<bool> DeleteByIdAsync(dynamic id);
Task<bool> DeleteByIdsAsync(dynamic[] ids);
#endregion
}
public interface ISqlSugarRepository<TEntity, TKey> : ISqlSugarRepository<TEntity>,IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, new()
{
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
public interface ISugarDbContextProvider<TDbContext>
where TDbContext : ISqlSugarDbContext
{
Task<TDbContext> GetDbContextAsync();
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
[AttributeUsage(AttributeTargets.Class)]
public class IgnoreCodeFirstAttribute : Attribute
{
}
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<ItemGroup>
<PackageReference Include="SqlSugarCoreNoDrive" Version="$(SqlSugarVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,11 +0,0 @@
using Volo.Abp.Modularity;
using Yi.Framework.Core;
namespace Yi.Framework.SqlSugarCore.Abstractions
{
[DependsOn(typeof(YiFrameworkCoreModule))]
public class YiFrameworkSqlSugarCoreAbstractionsModule : AbpModule
{
}
}

View File

@@ -1,19 +0,0 @@
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore
{
public class AsyncLocalDbContextAccessor
{
public static AsyncLocalDbContextAccessor Instance { get; } = new();
public ISqlSugarDbContext? Current
{
get => _currentScope.Value;
set => _currentScope.Value = value;
}
public AsyncLocalDbContextAccessor()
{
_currentScope = new AsyncLocal<ISqlSugarDbContext?>();
}
private readonly AsyncLocal<ISqlSugarDbContext> _currentScope;
}
}

View File

@@ -1,154 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using SqlSugar;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore.Repositories
{
public class SqlSugarObjectRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
public ISqlSugarClient _Db => GetDbContextAsync().Result;
private ISugarDbContextProvider<ISqlSugarDbContext> _sugarDbContextProvider;
/// <summary>
/// 获取DB
/// </summary>
/// <returns></returns>
public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
//await Console.Out.WriteLineAsync("获取的id" + db.ContextID);
return db;
}
public IAsyncQueryableExecuter AsyncExecuter => throw new NotImplementedException();
public bool? IsChangeTrackingEnabled => throw new NotImplementedException();
public Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> GetQueryableAsync()
{
throw new NotImplementedException();
}
public async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
await (await GetDbContextAsync()).InsertableByObject(entity).ExecuteCommandAsync();
return entity;
}
public Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public IQueryable<TEntity> WithDetails()
{
throw new NotImplementedException();
}
public IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync()
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
}
public class SqlSugarObjectRepository<TEntity, TKey> : SqlSugarObjectRepository<TEntity>, IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
{
public Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TEntity?> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,435 +0,0 @@
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Microsoft.Extensions.Logging;
using Nito.AsyncEx;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Yi.Framework.Core.Helper;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore.Repositories
{
public class SqlSugarRepository<TEntity> : ISqlSugarRepository<TEntity>, IRepository<TEntity> where TEntity : class, IEntity, new()
{
public ISqlSugarClient _Db => AsyncContext.Run(async () => await GetDbContextAsync());
public ISugarQueryable<TEntity> _DbQueryable => _Db.Queryable<TEntity>();
private ISugarDbContextProvider<ISqlSugarDbContext> _sugarDbContextProvider;
public IAsyncQueryableExecuter AsyncExecuter { get; }
public bool? IsChangeTrackingEnabled => false;
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider)
{
_sugarDbContextProvider = sugarDbContextProvider;
}
/// <summary>
/// 获取DB
/// </summary>
/// <returns></returns>
public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
return db;
}
/// <summary>
/// 获取简单Db
/// </summary>
/// <returns></returns>
public virtual async Task<SimpleClient<TEntity>> GetDbSimpleClientAsync()
{
var db = await GetDbContextAsync();
return new SimpleClient<TEntity>(db);
}
#region Abp模块
public virtual async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public virtual async Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public IQueryable<TEntity> WithDetails()
{
throw new NotImplementedException();
}
public IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync()
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> GetQueryableAsync()
{
throw new NotImplementedException();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetListAsync(predicate);
}
public virtual async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
return await InsertReturnEntityAsync(entity);
}
public virtual async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
await InsertRangeAsync(entities.ToList());
}
public virtual async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
await UpdateAsync(entity);
return entity;
}
public virtual async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
await UpdateRangeAsync(entities.ToList());
}
public virtual async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteAsync(entity);
}
public virtual async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteAsync(entities.ToList());
}
public virtual async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetListAsync();
}
public virtual async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await this.CountAsync(_=>true);
}
public virtual async Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetPageListAsync(_ => true, skipCount, maxResultCount);
}
#endregion
#region DB快捷操作
public virtual async Task<IDeleteable<TEntity>> AsDeleteable()
{
return (await GetDbSimpleClientAsync()).AsDeleteable();
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(List<TEntity> insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity insertObj)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObj);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity[] insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<ISugarQueryable<TEntity>> AsQueryable()
{
return (await GetDbSimpleClientAsync()).AsQueryable();
}
public virtual async Task<ISqlSugarClient> AsSugarClient()
{
return (await GetDbSimpleClientAsync()).AsSugarClient();
}
public virtual async Task<ITenant> AsTenant()
{
return (await GetDbSimpleClientAsync()).AsTenant();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(List<TEntity> updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity updateObj)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObj);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable()
{
return (await GetDbSimpleClientAsync()).AsUpdateable();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity[] updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
#endregion
#region SimpleClient模块
public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).CountAsync(whereExpression);
}
public virtual async Task<bool> DeleteAsync(TEntity deleteObj)
{
if (deleteObj is ISoftDelete)
{
ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, deleteObj);
return await (await GetDbSimpleClientAsync()).UpdateAsync(deleteObj);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObj);
}
}
public virtual async Task<bool> DeleteAsync(List<TEntity> deleteObjs)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
deleteObjs.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(deleteObjs);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObjs);
}
}
public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
return await (await GetDbSimpleClientAsync()).AsUpdateable().SetColumns(nameof(ISoftDelete.IsDeleted), true).Where(whereExpression).ExecuteCommandAsync() > 0;
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(whereExpression);
}
}
public virtual async Task<bool> DeleteByIdAsync(dynamic id)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var entity = await GetByIdAsync(id);
//反射赋值
ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, entity);
return await UpdateAsync(entity);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(id);
}
}
public virtual async Task<bool> DeleteByIdsAsync(dynamic[] ids)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var simpleClient = (await GetDbSimpleClientAsync());
var entities = await simpleClient.AsQueryable().In(ids).ToListAsync();
if (entities.Count == 0)
{
return false;
}
//反射赋值
entities.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await UpdateRangeAsync(entities);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(ids);
}
}
public virtual async Task<TEntity> GetByIdAsync(dynamic id)
{
return await (await GetDbSimpleClientAsync()).GetByIdAsync(id);
}
public virtual async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetFirstAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetListAsync()
{
return await (await GetDbSimpleClientAsync()).GetListAsync();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetListAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression, int pageNum, int pageSize)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression, new PageModel() { PageIndex = pageNum, PageSize = pageSize });
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression, int pageNum, int pageSize, Expression<Func<TEntity, object>>? orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression, new PageModel { PageIndex = pageNum, PageSize = pageSize }, orderByExpression, orderByType);
}
public virtual async Task<TEntity> GetSingleAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetSingleAsync(whereExpression);
}
public virtual async Task<bool> InsertAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertAsync(insertObj);
}
public virtual async Task<bool> InsertOrUpdateAsync(TEntity data)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(data);
}
public virtual async Task<bool> InsertOrUpdateAsync(List<TEntity> datas)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(datas);
}
public virtual async Task<bool> InsertRangeAsync(List<TEntity> insertObjs)
{
return await (await GetDbSimpleClientAsync()).InsertRangeAsync(insertObjs);
}
public virtual async Task<long> InsertReturnBigIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnBigIdentityAsync(insertObj);
}
public virtual async Task<TEntity> InsertReturnEntityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnEntityAsync(insertObj);
}
public virtual async Task<int> InsertReturnIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnIdentityAsync(insertObj);
}
public virtual async Task<long> InsertReturnSnowflakeIdAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnSnowflakeIdAsync(insertObj);
}
public virtual async Task<bool> IsAnyAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).IsAnyAsync(whereExpression);
}
public virtual async Task<bool> UpdateAsync(TEntity updateObj)
{
if (typeof(TEntity).IsAssignableTo<IHasConcurrencyStamp>())//带版本号乐观锁更新
{
try
{
int num = await (await GetDbSimpleClientAsync())
.Context.Updateable(updateObj).ExecuteCommandWithOptLockAsync(true);
return num>0;
}
catch (VersionExceptions ex)
{
throw new AbpDbConcurrencyException($"{ex.Message}[更新失败ConcurrencyStamp不是最新版本],entityInfo{updateObj}", ex);
}
}
return await (await GetDbSimpleClientAsync()).UpdateAsync(updateObj);
}
public virtual async Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).UpdateAsync(columns, whereExpression);
}
public virtual async Task<bool> UpdateRangeAsync(List<TEntity> updateObjs)
{
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(updateObjs);
}
#endregion
}
public class SqlSugarRepository<TEntity, TKey> : SqlSugarRepository<TEntity>, ISqlSugarRepository<TEntity, TKey>, IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, new()
{
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider) : base(sugarDbContextProvider)
{
}
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteByIdAsync(id);
}
public virtual async Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteByIdsAsync(ids.Select(x => (object)x).ToArray());
}
public virtual async Task<TEntity?> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
public virtual async Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
}
}

View File

@@ -1,123 +0,0 @@
using System.Reflection;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore
{
public class SqlSugarDbConnectionCreator: ISqlSugarDbConnectionCreator,ITransientDependency
{
public SqlSugarDbConnectionCreator(IOptions<DbConnOptions> options)
{
Options = options.Value;
}
public DbConnOptions Options { get; }
public void SetDbAop(ISqlSugarClient currentDb)
{
currentDb.Aop.OnLogExecuting = this.OnLogExecuting;
currentDb.Aop.OnLogExecuted = this.OnLogExecuted;
currentDb.Aop.DataExecuting = this.DataExecuting;
currentDb.Aop.DataExecuted = this.DataExecuted;
OnSqlSugarClientConfig(currentDb);
}
public ConnectionConfig Build(Action<ConnectionConfig>? action=null)
{
var dbConnOptions = Options;
#region options
if (dbConnOptions.DbType is null)
{
throw new ArgumentException("DbType配置为空");
}
var slavaConFig = new List<SlaveConnectionConfig>();
if (dbConnOptions.EnabledReadWrite)
{
if (dbConnOptions.ReadUrl is null)
{
throw new ArgumentException("读写分离为空");
}
var readCon = dbConnOptions.ReadUrl;
readCon.ForEach(s =>
{
//如果是动态saas分库这里的连接串都不能写死需要动态添加这里只配置共享库的连接
slavaConFig.Add(new SlaveConnectionConfig() { ConnectionString = s });
});
}
#endregion
#region config
var connectionConfig = new ConnectionConfig()
{
ConfigId= ConnectionStrings.DefaultConnectionStringName,
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
ConnectionString = dbConnOptions.Url,
IsAutoCloseConnection = true,
SlaveConnectionConfigs = slavaConFig,
//设置codefirst非空值判断
ConfigureExternalServices = new ConfigureExternalServices
{
// 处理表
EntityNameService = (type, entity) =>
{
if (dbConnOptions.EnableUnderLine && !entity.DbTableName.Contains('_'))
entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName);// 驼峰转下划线
},
EntityService = (c, p) =>
{
if (new NullabilityInfoContext()
.Create(c).WriteState is NullabilityState.Nullable)
{
p.IsNullable = true;
}
if (dbConnOptions.EnableUnderLine && !p.IsIgnore && !p.DbColumnName.Contains('_'))
p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);// 驼峰转下划线
EntityService(c, p);
}
},
//这里多租户有个坑,无效的
AopEvents = new AopEvents
{
DataExecuted = DataExecuted,
DataExecuting = DataExecuting,
OnLogExecuted = OnLogExecuted,
OnLogExecuting = OnLogExecuting
}
};
if (action is not null)
{
action.Invoke(connectionConfig);
}
#endregion
return connectionConfig;
}
[DisablePropertyInjection]
public Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
[DisablePropertyInjection]
public Action<object, DataAfterModel> DataExecuted { get; set; }
[DisablePropertyInjection]
public Action<object, DataFilterModel> DataExecuting { get; set; }
[DisablePropertyInjection]
public Action<string, SugarParameter[]> OnLogExecuting { get; set; }
[DisablePropertyInjection]
public Action<string, SugarParameter[]> OnLogExecuted { get; set; }
[DisablePropertyInjection]
public Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
}
}

View File

@@ -1,379 +0,0 @@
using System.Collections;
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
using Yi.Framework.SqlSugarCore.Abstractions;
using Check = Volo.Abp.Check;
namespace Yi.Framework.SqlSugarCore
{
public class SqlSugarDbContext : ISqlSugarDbContext
{
/// <summary>
/// SqlSugar 客户端
/// </summary>
public ISqlSugarClient SqlSugarClient { get; private set; }
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
private IAbpLazyServiceProvider LazyServiceProvider { get; }
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
private ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
private IEntityChangeEventHelper EntityChangeEventHelper =>
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
private ISerializeService SerializeService => LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
{
SqlSugarClient = sqlSugarClient;
}
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
{
LazyServiceProvider = lazyServiceProvider;
var connectionCreator = LazyServiceProvider.LazyGetRequiredService<ISqlSugarDbConnectionCreator>();
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
connectionCreator.EntityService = EntityService;
connectionCreator.DataExecuting = DataExecuting;
connectionCreator.DataExecuted = DataExecuted;
connectionCreator.OnLogExecuting = OnLogExecuting;
connectionCreator.OnLogExecuted = OnLogExecuted;
SqlSugarClient = new SqlSugarClient(connectionCreator.Build(action: options =>
{
options.ConnectionString = GetCurrentConnectionString();
options.DbType = GetCurrentDbType();
}));
//统一使用aop处理
connectionCreator.SetDbAop(SqlSugarClient);
//替换默认序列化器
SqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SerializeService = SerializeService;
}
/// <summary>
/// db切换多库支持
/// </summary>
/// <returns></returns>
protected virtual string GetCurrentConnectionString()
{
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
var connectionString =
connectionStringResolver.ResolveAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (string.IsNullOrWhiteSpace(connectionString))
{
Check.NotNull(Options.Url, "dbUrl未配置");
}
return connectionString!;
}
protected virtual DbType GetCurrentDbType()
{
if (CurrentTenant.Name is not null)
{
var dbTypeFromTenantName = GetDbTypeFromTenantName(CurrentTenant.Name);
if (dbTypeFromTenantName is not null)
{
return dbTypeFromTenantName.Value;
}
}
Check.NotNull(Options.DbType, "默认DbType未配置");
return Options.DbType!.Value;
}
//根据租户name进行匹配db类型: Test_Sqlite[来自AI]
private DbType? GetDbTypeFromTenantName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
// 查找下划线的位置
int underscoreIndex = name.LastIndexOf('_');
if (underscoreIndex == -1 || underscoreIndex == name.Length - 1)
{
return null;
}
// 提取 枚举 部分
string enumString = name.Substring(underscoreIndex + 1);
// 尝试将 尾缀 转换为枚举
if (Enum.TryParse<DbType>(enumString, out DbType result))
{
return result;
}
// 条件不满足时返回 null
return null;
}
/// <summary>
/// 上下文对象扩展
/// </summary>
/// <param name="sqlSugarClient"></param>
protected virtual void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
{
//需自定义扩展
if (IsSoftDeleteFilterEnabled)
{
sqlSugarClient.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
}
if (IsMultiTenantFilterEnabled)
{
//表达式里只能有具体值,不能运算
var expressionCurrentTenant = CurrentTenant.Id ?? null;
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == expressionCurrentTenant);
}
CustomDataFilter(sqlSugarClient);
}
protected virtual void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
}
protected virtual void DataExecuted(object oldValue, DataAfterModel entityInfo)
{
}
/// <summary>
/// 数据
/// </summary>
/// <param name="oldValue"></param>
/// <param name="entityInfo"></param>
protected virtual void DataExecuting(object oldValue, DataFilterModel entityInfo)
{
//审计日志
switch (entityInfo.OperationType)
{
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
{
if (!DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
{
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
{
if (CurrentUser.Id != null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
}
break;
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName.Equals(nameof(IEntity<Guid>.Id)))
{
//类型为guid
if (typeof(Guid) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
{
//主键为空或者为默认最小值
if (Guid.Empty.Equals(oldValue))
{
entityInfo.SetValue(GuidGenerator.Create());
}
}
}
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
{
//为空或者为默认最小值
if (DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
{
//类型为guid
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
{
if (CurrentUser.Id is not null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
}
else if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
{
if (CurrentTenant.Id is not null)
{
entityInfo.SetValue(CurrentTenant.Id);
}
}
break;
}
//领域事件
switch (entityInfo.OperationType)
{
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
EntityChangeEventHelper.PublishEntityCreatedEvent(entityInfo.EntityValue);
}
break;
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
//软删除,发布的是删除事件
if (entityInfo.EntityValue is ISoftDelete softDelete)
{
if (softDelete.IsDeleted == true)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
}
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityInfo.EntityValue);
}
}
break;
case DataFilterType.DeleteByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
//这里sqlsugar有个特殊删除会返回批量的结果
if (entityInfo.EntityValue is IEnumerable entityValues)
{
foreach (var entityValue in entityValues)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityValue);
}
}
}
break;
}
}
/// <summary>
/// 日志
/// </summary>
/// <param name="sql"></param>
/// <param name="pars"></param>
protected virtual void OnLogExecuting(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("==========Yi-SQL执行:==========");
sb.AppendLine(UtilMethods.GetSqlString(DbType.SqlServer, sql, pars));
sb.AppendLine("===============================");
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sb.ToString());
}
}
/// <summary>
/// 日志
/// </summary>
/// <param name="sql"></param>
/// <param name="pars"></param>
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
var sqllog = $"=========Yi-SQL耗时{SqlSugarClient.Ado.SqlExecutionTime.TotalMilliseconds}毫秒=====";
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sqllog.ToString());
}
}
/// <summary>
/// 实体配置
/// </summary>
/// <param name="property"></param>
/// <param name="column"></param>
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
{
if (property.Name == nameof(IHasConcurrencyStamp.ConcurrencyStamp)) //带版本号并发更新
{
column.IsEnableUpdateVersionValidation = true;
}
if (property.PropertyType == typeof(ExtraPropertyDictionary))
{
column.IsIgnore = true;
}
if (property.Name == nameof(Entity<object>.Id))
{
column.IsPrimarykey = true;
}
}
public void BackupDataBase()
{
string directoryName = "database_backup";
string fileName = DateTime.Now.ToString($"yyyyMMdd_HHmmss") + $"_{SqlSugarClient.Ado.Connection.Database}";
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
switch (Options.DbType)
{
case DbType.MySql:
//MySql
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.sql"); //mysql 只支持.net core
break;
case DbType.Sqlite:
//Sqlite
SqlSugarClient.DbMaintenance.BackupDataBase(null, $"{fileName}.db"); //sqlite 只支持.net core
break;
case DbType.SqlServer:
//SqlServer
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.bak" /*服务器路径*/); //第一个参数库名
break;
default:
throw new NotImplementedException("其他数据库备份未实现");
}
}
}
}

View File

@@ -1,29 +0,0 @@
using System.Data.Common;
using Volo.Abp;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore;
public class SqlSugarDbContextCreationContext
{
public static SqlSugarDbContextCreationContext Current => _current.Value;
private static readonly AsyncLocal<SqlSugarDbContextCreationContext> _current = new AsyncLocal<SqlSugarDbContextCreationContext>();
public string ConnectionStringName { get; }
public string ConnectionString { get; }
public DbConnection ExistingConnection { get; internal set; }
public SqlSugarDbContextCreationContext(string connectionStringName, string connectionString)
{
ConnectionStringName = connectionStringName;
ConnectionString = connectionString;
}
public static IDisposable Use(SqlSugarDbContextCreationContext context)
{
var previousValue = Current;
_current.Value = context;
return new DisposeAction(() => _current.Value = previousValue);
}
}

View File

@@ -1,76 +0,0 @@
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using SqlSugar;
namespace Yi.Framework.SqlSugarCore;
public class NonPublicPropertiesResolver : DefaultContractResolver
{
/// <summary>
/// 重写获取属性存在get set方法就可以写入
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo pi)
{
prop.Readable = (pi.GetMethod != null);
prop.Writable = (pi.SetMethod != null);
}
return prop;
}
}
public class SqlSugarNonPublicSerializer : ISerializeService
{
/// <summary>
/// 默认的序列化服务
/// </summary>
private readonly ISerializeService _serializeService = DefaultServices.Serialize;
public string SerializeObject(object value)
{
//保留原有实现
return _serializeService.SerializeObject(value);
}
public string SugarSerializeObject(object value)
{ //保留原有实现
return _serializeService.SugarSerializeObject(value);
}
/// <summary>
/// 重写对象反序列化支持NoPublic访问器
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T DeserializeObject<T>(string value)
{
if (typeof(T).FullName.StartsWith("System.Text.Json."))
{
// 动态创建一个 JsonSerializer 实例
Type serializerType =typeof(T).Assembly.GetType("System.Text.Json.JsonSerializer");
var methods = serializerType
.GetMethods().Where(it=>it.Name== "Deserialize")
.Where(it=>it.GetParameters().Any(z=>z.ParameterType==typeof(string))).First();
// 调用 SerializeObject 方法序列化对象
T json = (T)methods.MakeGenericMethod(typeof(T))
.Invoke(null, new object[] { value, null });
return json;
}
var jSetting = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver =new NonPublicPropertiesResolver() //替换默认解析器使能支持protect
};
return JsonConvert.DeserializeObject<T>(value, jSetting);
}
}

Some files were not shown because too many files have changed in this diff Show More