Compare commits

...

58 Commits
main ... dev

Author SHA1 Message Date
lihanbo e2ccb07b51 重构权限检查逻辑
将权限检查逻辑移至 `progName` 不为 null 的条件下,并在此条件下调用 `next()`。调整了对 OPTIONS 请求的处理,确保在权限不足时返回相应的错误信息。
2025-03-10 17:58:37 +08:00
Ling fa160cac83 105040 用户权限项 2024-11-13 16:23:30 +08:00
lihanbo 4c60372f04 Merge branch 'dev' of http://192.168.1.144:8859/SinvoCSharp/LFlow.PluginAdmin into dev 2024-11-13 13:36:19 +08:00
lihanbo f726fa20d8 105040 增加注释 2024-11-13 13:36:09 +08:00
SINVO\yangshunli 4caab82c40 105067 使用appsetting中的配置注入MinIo 2024-11-06 15:56:28 +08:00
lihanbo 3024bb1d62 105040 切换Minio服务 2024-11-04 16:46:09 +08:00
lihanbo a8f1fab64c 105040 移除缓存文件 2024-11-04 16:45:49 +08:00
lihanbo d00c73d658 Merge branch 'dev' of http://192.168.1.144:8859/SinvoCSharp/LFlow.PluginAdmin into dev
# Conflicts:
#	LFlow.Base/appsettings.json
2024-11-04 15:37:01 +08:00
lihanbo 06ca3270a7 105040 调整调试端口
105040 调整调试端口
2024-11-04 15:35:30 +08:00
lihanbo fccb6f16c2 105040 调整调试端口 2024-11-04 15:33:38 +08:00
lihanbo b66fda778e 105040 调整文件上传接口 2024-11-04 15:32:23 +08:00
lihanbo 3f6c7439bc Merge branch 'dev' of http://192.168.1.144:8859/SinvoCSharp/LFlow.PluginAdmin into dev 2024-11-04 15:14:50 +08:00
lihanbo 2f28877a30 105040 增加注释 2024-11-04 15:14:38 +08:00
lihanbo 84a41b1a9c 105040 增加Token字段 2024-11-04 15:14:20 +08:00
lihanbo f64b4e47c3 105040 调整中间件优先级,处理预检请求 2024-11-04 15:13:58 +08:00
SINVO\yangshunli 52be00103c 105067 MinIO调试 2024-11-04 15:11:14 +08:00
lihanbo 873d7bbd01 105040 Update 注释 2024-11-04 10:10:05 +08:00
lihanbo dc2f90e66b 105040 增加程序入口读取接口,用于自动配置权限项 2024-11-02 10:39:14 +08:00
lihanbo ff35047431 105040 Clean code 2024-11-02 10:38:43 +08:00
lihanbo cb8b1c5de9 105040 Update Swagger文档分组支持 2024-11-02 10:37:54 +08:00
lihanbo 97bc99e853 105040 Fix 初始化表错误 2024-11-01 17:11:44 +08:00
lihanbo 1f0cd62488 105040 Add 权限服务,角色服务 2024-11-01 16:46:18 +08:00
lihanbo 2a91923955 105040 Rename 中间件接口 2024-11-01 16:45:39 +08:00
lihanbo e36444b9b8 105040 Add 用户登录后缓存 2024-10-31 17:49:25 +08:00
lihanbo 8931360620 105040 Add 异步发布事件接口 2024-10-31 17:48:58 +08:00
lihanbo 5ff1390e60 105040 Add 内存缓存支持 2024-10-31 17:48:11 +08:00
lihanbo dafd320b05 105040 Update nuget package 2024-10-31 15:00:29 +08:00
lihanbo eeb3fece21 105040 Update 移除引用,改为Web项目 2024-10-31 14:59:42 +08:00
lihanbo f646ae4b4b 105040 Update 使用可空类型 2024-10-31 14:59:06 +08:00
lihanbo f26ea7288b 105040 Add 增加事件机制 2024-10-31 11:41:49 +08:00
lihanbo 6d135ba074 105040 Update 更新中间件描述 2024-10-31 11:41:20 +08:00
lihanbo ae37631d03 105040 Update 添加中间件支持 2024-10-31 10:38:21 +08:00
lihanbo 9574d7e0b6 105040 Update 增加缓存与压缩 2024-10-31 10:36:56 +08:00
lihanbo 0d0a314ef9 105040 Update 返回结果使用可空类型 2024-10-31 10:36:17 +08:00
lihanbo 713e57c424 105040 Add 用户管理模块 2024-10-29 15:46:44 +08:00
lihanbo 04f8d9e15d Add 增加扩展函数 2024-10-29 15:46:23 +08:00
lihanbo cc23f8b00b Fix Sqlsugar 从实体特性中读取主键自增列信息 2024-10-29 15:45:47 +08:00
lihanbo 03d1345262 Fix 默认的增删改查实现有误 2024-10-29 15:45:30 +08:00
lihanbo 4084e01c31 105040 增加封装函数,直接构造返回结果 2024-10-29 14:40:07 +08:00
lihanbo 548cc42d60 删除 LFlow-dev.db 2024-10-22 10:14:47 +08:00
lihanbo 61c853229a Merge branch 'dev' of http://192.168.1.144:8859/SinvoCSharp/LFlow.PluginAdmin into dev 2024-10-22 10:14:21 +08:00
lihanbo 5ed583a006 105040 Update 2024-10-22 10:14:13 +08:00
lihanbo aa79da6910 更新 .gitignore 2024-10-22 10:13:38 +08:00
lihanbo 250ba4a01a 增加在线管理模块 2024-10-19 08:33:19 +08:00
lihanbo 0a217e7b50 105040 打印Debug信息 2024-10-19 08:33:04 +08:00
lihanbo 4e17fa54c1 105040 调整可见性 2024-10-19 08:32:33 +08:00
lihanbo d6e19c291b 开发测试数据库提交 2024-10-18 11:33:56 +08:00
lihanbo 01391809a4 清理无用代码 2024-10-18 11:33:39 +08:00
lihanbo cb1ef1db08 增加获取最新版本接口 2024-10-18 11:33:00 +08:00
lihanbo b5e1badf07 调整字段信息,增加字段最后修改时间 2024-10-18 11:32:45 +08:00
lihanbo 2ddef0d1b5 增加控制器,部分逻辑转为自实现 2024-10-18 11:32:06 +08:00
lihanbo 578f73a23a 调整日志 2024-10-18 11:31:15 +08:00
lihanbo e37939e82b 增加前端文件夹 2024-10-18 11:30:41 +08:00
lihanbo 4c6b9b567a 配置静态文件与首页路径 2024-10-18 11:30:00 +08:00
lihanbo f171251921 配置跨域策略 2024-10-18 11:29:40 +08:00
lihanbo 47db444117 清理导入 2024-10-18 11:29:02 +08:00
lihanbo e2ed876d9b 返回结果增加状态码,与前端对接 2024-10-18 11:24:31 +08:00
lihanbo 8439531c54 移除IService中的约束,不再强制要求实现接口,由各模块自行实现 2024-10-18 11:23:51 +08:00
79 changed files with 3409 additions and 157 deletions

2
.gitignore vendored
View File

@ -1,4 +1,3 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
@ -289,3 +288,4 @@ __pycache__/
*.odx.cs
*.xsd.cs
/LF[Ll]ow_Bin/
LFlow-dev.db

View File

@ -1,4 +1,4 @@
using System;
using System.Reflection;
namespace LFlow.Base;
/// <summary>
@ -6,10 +6,25 @@ namespace LFlow.Base;
/// </summary>
public class App
{
public static IServiceCollection? services;
/// <summary>
/// 服务集合
/// </summary>
internal static IServiceCollection? services;
/// <summary>
/// 服务提供者
/// </summary>
private static IServiceProvider? ServiceProvider => services?.BuildServiceProvider();
/// <summary>
/// 获取服务
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? GetService<T>()
{
return ServiceProvider!.GetService<T>();
}
/// <summary>
/// 子服务程序集
/// </summary>
public static List<Assembly> SubServiceAssembly = [];
}

516
LFlow.Base/Base.xml Normal file
View File

@ -0,0 +1,516 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>LFlow.Base</name>
</assembly>
<members>
<member name="T:LFlow.Base.App">
<summary>
程序对象
</summary>
</member>
<member name="F:LFlow.Base.App.services">
<summary>
服务集合
</summary>
</member>
<member name="P:LFlow.Base.App.ServiceProvider">
<summary>
服务提供者
</summary>
</member>
<member name="M:LFlow.Base.App.GetService``1">
<summary>
获取服务
</summary>
<typeparam name="T"></typeparam>
<returns></returns>
</member>
<member name="F:LFlow.Base.App.SubServiceAssembly">
<summary>
子服务程序集
</summary>
</member>
<member name="T:LFlow.Base.Default.DefaultCurdRepo`2">
<summary>
默认的增删改查仓储
</summary>
<typeparam name="T"></typeparam>
<typeparam name="K"></typeparam>
</member>
<member name="F:LFlow.Base.Default.DefaultCurdRepo`2._client">
<summary>
数据库连接客户端
</summary>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.#ctor(SqlSugar.ISqlSugarClient)">
<summary>
构造函数
</summary>
<param name="client"></param>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.DeleteById(`1)">
<summary>
根据ID删除对象
</summary>
<param name="id"></param>
<returns></returns>
<exception cref="T:System.Exception"></exception>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.Get(`1)">
<summary>
根据ID获取对象
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.GetAll(System.Int32,System.Int32,System.Int32@)">
<summary>
批量查询(分页)
</summary>
<param name="pageIndex"></param>
<param name="pageSize"></param>
<param name="pageTotal"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.SaveOrUpdate(`0,System.Boolean)">
<summary>
报错或是更新对象
</summary>
<param name="entity"></param>
<param name="isUpdate"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.Search(`0)">
<summary>
搜索
</summary>
<param name="whereObj"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Default.DefaultCurdRepo`2.WhereSearchId(`0)">
<summary>
查找ID
</summary>
<param name="whereObj"></param>
<returns></returns>
</member>
<member name="T:LFlow.Base.Interfaces.BaseController">
<summary>
基础控制器
</summary>
<example> Route("api/[controller]/[action]") ApiController </example>
</member>
<member name="M:LFlow.Base.Interfaces.BaseController.Success``1(``0)">
<summary>
成功返回
</summary>
<typeparam name="T"></typeparam>
<param name="data"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.BaseController.Success``1(``0,System.Int32,System.Int32,System.Int32)">
<summary>
成功返回(分页)
</summary>
<typeparam name="T"></typeparam>
<param name="data"></param>
<param name="totalCount"></param>
<param name="pageIndex"></param>
<param name="pageSize"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.BaseController.Fail``1(``0,System.String,System.Int32)">
<summary>
失败返回
</summary>
<typeparam name="T"></typeparam>
<param name="data"></param>
<param name="errorMsg"></param>
<param name="errCode"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.BaseController.PagedFail``1(``0,System.String,System.Int32)">
<summary>
失败返回(分页)
</summary>
<typeparam name="T"></typeparam>
<param name="data"></param>
<param name="errorMsg"></param>
<param name="errCode"></param>
<returns></returns>
</member>
<member name="T:LFlow.Base.Interfaces.IController">
<summary>
控制器接口
</summary>
</member>
<member name="T:LFlow.Base.Interfaces.IDataModel">
<summary>
数据模型接口 Repo层用
</summary>
</member>
<member name="P:LFlow.Base.Interfaces.IDataModel.ID">
<summary>
ID
</summary>
</member>
<member name="T:LFlow.Base.Interfaces.IModel">
<summary>
模型顶层接口 Service层用
</summary>
</member>
<member name="T:LFlow.Base.Interfaces.IModule">
<summary>
模块接口
</summary>
</member>
<member name="M:LFlow.Base.Interfaces.IModule.ConfigureModule(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
配置模块
</summary>
<param name="services"></param>
</member>
<member name="T:LFlow.Base.Interfaces.IRepo`2">
<summary>
通用的仓库对象接口
</summary>
<typeparam name="T">数据模型</typeparam>
<typeparam name="K">主键</typeparam>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.Get(`1)">
<summary>
获取单个对象
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.DeleteById(`1)">
<summary>
删除单个对象
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.SaveOrUpdate(`0,System.Boolean)">
<summary>
保存与更新
</summary>
<param name="entity"></param>
<param name="isUpdate"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.GetAll(System.Int32,System.Int32,System.Int32@)">
<summary>
获取所有对象列表(默认分页)
</summary>
<param name="pageIndex"></param>
<param name="pageSize"></param>
<param name="pageTotal"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.Search(`0)">
<summary>
根据条件搜索对象列表
</summary>
<param name="whereObj"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Interfaces.IRepo`2.WhereSearchId(`0)">
<summary>
根据条件搜索主键列表
</summary>
<param name="whereObj"></param>
<returns></returns>
</member>
<member name="T:LFlow.Base.Interfaces.IService">
<summary>
服务接口
</summary>
</member>
<member name="T:LFlow.Base.Program">
<summary>
</summary>
</member>
<member name="M:LFlow.Base.Program.Main(System.String[])">
<summary>
入口
</summary>
<param name="args"></param>
</member>
<member name="M:LFlow.Base.Program.ConfigureSqlSugar(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
配置SqlSugar
</summary>
<param name="services"></param>
</member>
<member name="M:LFlow.Base.Program.LoadSubService(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
从子文件夹中加载DLL
</summary>
</member>
<member name="M:LFlow.Base.Program.ConfigDllLoader">
<summary>
配置DLL加载器
接管DLL加载过程处理依赖问题从当前路径和Service文件夹中加载DLL
</summary>
</member>
<member name="T:LFlow.Base.Utils.ApiExplorerGroupPerVersionConvention">
<summary>
控制器模型约定
</summary>
</member>
<member name="M:LFlow.Base.Utils.ApiExplorerGroupPerVersionConvention.Apply(Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel)">
<summary>
配置
</summary>
<param name="controller"></param>
</member>
<member name="T:LFlow.Base.Utils.ApiResult`1">
<summary>
返回结果包装
</summary>
<typeparam name="T"></typeparam>
</member>
<member name="P:LFlow.Base.Utils.ApiResult`1.Data">
<summary>
数据
</summary>
</member>
<member name="M:LFlow.Base.Utils.ApiResult`1.#ctor">
<summary>
构造函数
</summary>
</member>
<member name="M:LFlow.Base.Utils.ApiResult`1.#ctor(System.Boolean,System.String,System.Int32,`0)">
<summary>
构造函数
</summary>
<param name="success"></param>
<param name="message"></param>
<param name="code"></param>
<param name="data"></param>
</member>
<member name="P:LFlow.Base.Utils.ApiResult`1.Success">
<summary>
是否成功
</summary>
</member>
<member name="P:LFlow.Base.Utils.ApiResult`1.Message">
<summary>
消息
</summary>
</member>
<member name="P:LFlow.Base.Utils.ApiResult`1.Code">
<summary>
状态码
</summary>
</member>
<member name="M:LFlow.Base.Utils.ApiResult`1.SuccessResult(`0,System.String,System.Int32)">
<summary>
成功返回
</summary>
<param name="data"></param>
<param name="message"></param>
<param name="code"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.ApiResult`1.FailResult(System.String,System.Int32)">
<summary>
失败返回
</summary>
<param name="message"></param>
<param name="code"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.BucketManager.IsExistStr(System.String)">
<summary>
1、判断bucket是否存在
</summary>
<param name="bucketName"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.BucketManager.Create(System.String)">
<summary>
2、创建一个bucket
</summary>
<param name="bucketName"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.BucketManager.Delete(System.String)">
<summary>
3、移除一个bucket
</summary>
<param name="bucketName"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.BucketManager.GetList">
<summary>
4、获取已有的bucket列表
</summary>
<returns></returns>
</member>
<member name="T:LFlow.Base.Utils.CodeFirst">
<summary>
CodeFirst
</summary>
</member>
<member name="F:LFlow.Base.Utils.CodeFirst._types">
<summary>
类型集合
</summary>
</member>
<member name="M:LFlow.Base.Utils.CodeFirst.AddType(System.Type)">
<summary>
添加类型
</summary>
<param name="type"></param>
</member>
<member name="M:LFlow.Base.Utils.CodeFirst.InitTable">
<summary>
初始化表
</summary>
</member>
<member name="M:LFlow.Base.Utils.CodeFirst.InitDBSeed">
<summary>
初始化数据库种子
</summary>
</member>
<member name="T:LFlow.Base.Utils.Mapper">
<summary>
映射器
</summary>
</member>
<member name="M:LFlow.Base.Utils.Mapper.Map``1(System.Object)">
<summary>
将一个对象映射到另一个对象
</summary>
<typeparam name="T"></typeparam>
<param name="source"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.Mapper.MapTo``1(LFlow.Base.Interfaces.IDataModel)">
<summary>
将一个数据模型映射到另一个对象
</summary>
<typeparam name="T"></typeparam>
<param name="model"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.MinIOService.GetBuckets">
<summary>
获取桶列表
</summary>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.MinIOService.DownLoad">
<summary>
下载接口
</summary>
<returns></returns>
</member>
<member name="T:LFlow.Base.Utils.ObjectToWhereExp">
<summary>
对象转换为where条件
</summary>
</member>
<member name="M:LFlow.Base.Utils.ObjectToWhereExp.ToWhereExp(LFlow.Base.Interfaces.IModel)">
<summary>
将IModel对象转换为where条件
</summary>
<param name="model"></param>
<returns></returns>
</member>
<member name="T:LFlow.Base.Utils.PagedApiResult`1">
<summary>
返回结果包装 (分页)
</summary>
<typeparam name="T"></typeparam>
</member>
<member name="P:LFlow.Base.Utils.PagedApiResult`1.Data">
<summary>
数据
</summary>
</member>
<member name="P:LFlow.Base.Utils.PagedApiResult`1.TotalCount">
<summary>
总行数
</summary>
</member>
<member name="P:LFlow.Base.Utils.PagedApiResult`1.PageIndex">
<summary>
当前页
</summary>
</member>
<member name="P:LFlow.Base.Utils.PagedApiResult`1.PageSize">
<summary>
每页行数
</summary>
</member>
<member name="M:LFlow.Base.Utils.PagedApiResult`1.#ctor">
<summary>
构造函数
</summary>
</member>
<member name="M:LFlow.Base.Utils.PagedApiResult`1.#ctor(System.Boolean,System.String,System.Int32,`0,System.Int32,System.Int32,System.Int32)">
<summary>
构造函数
</summary>
<param name="success"></param>
<param name="message"></param>
<param name="code"></param>
<param name="data"></param>
<param name="totalCount"></param>
<param name="pageIndex"></param>
<param name="pageSize"></param>
</member>
<member name="M:LFlow.Base.Utils.PagedApiResult`1.SuccessResult(`0,System.Int32,System.Int32,System.Int32,System.String,System.Int32)">
<summary>
成功返回
</summary>
<param name="data"></param>
<param name="totalCount"></param>
<param name="pageIndex"></param>
<param name="pageSize"></param>
<param name="message"></param>
<param name="code"></param>
<returns></returns>
</member>
<member name="M:LFlow.Base.Utils.PagedApiResult`1.FailResult(System.String,System.Int32)">
<summary>
失败返回
</summary>
<param name="message"></param>
<param name="errCode"></param>
<returns></returns>
</member>
<member name="T:LFlow.Base.Utils.RegisterModule">
<summary>
注册模块
</summary>
</member>
<member name="M:LFlow.Base.Utils.RegisterModule.RegisterAllModel(System.Collections.Generic.List{System.Type},Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
注册所有模型
</summary>
<param name="types"></param>
<param name="services"></param>
</member>
<member name="M:LFlow.Base.Utils.RegisterModule.RegisterAllService(System.Collections.Generic.List{System.Type},Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
注册所有服务
</summary>
<param name="types"></param>
<param name="services"></param>
</member>
<member name="M:LFlow.Base.Utils.RegisterModule.RegisterAllRepo(System.Collections.Generic.List{System.Type},Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
注册所有数据仓库
</summary>
<param name="types"></param>
<param name="services"></param>
</member>
</members>
</doc>

View File

@ -10,7 +10,14 @@ namespace LFlow.Base.Default;
/// <typeparam name="K"></typeparam>
public abstract class DefaultCurdRepo<T, K> : IRepo<T, K> where T : class, IDataModel, new()
{
/// <summary>
/// 数据库连接客户端
/// </summary>
private readonly ISqlSugarClient _client;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="client"></param>
public DefaultCurdRepo(ISqlSugarClient client)
{
_client = client;
@ -85,8 +92,8 @@ public abstract class DefaultCurdRepo<T, K> : IRepo<T, K> where T : class, IData
/// </summary>
/// <param name="whereObj"></param>
/// <returns></returns>
public virtual List<K> WhereSearchId(T whereObj)
public virtual List<string> WhereSearchId(T whereObj)
{
return _client.Queryable<T>().Where(whereObj.ToWhereExp()).Select(x => (K)Convert.ChangeType(x.ID, typeof(K))).ToList();
return _client.Queryable<T>().Where(whereObj.ToWhereExp()).Select(x => x.ID).ToList();
}
}

View File

@ -1,3 +1,4 @@
using LFlow.Base.Utils;
using Microsoft.AspNetCore.Mvc;
namespace LFlow.Base.Interfaces;
@ -10,5 +11,51 @@ namespace LFlow.Base.Interfaces;
[ApiController]
public abstract class BaseController : ControllerBase, IController
{
/// <summary>
/// 成功返回
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
protected virtual ApiResult<T> Success<T>(T? data) where T : class, new()
{
return ApiResult<T>.SuccessResult(data);
}
/// <summary>
/// 成功返回(分页)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="totalCount"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
protected virtual PagedApiResult<T> Success<T>(T? data, int totalCount, int pageIndex, int pageSize) where T : class, new()
{
return PagedApiResult<T>.SuccessResult(data, totalCount, pageIndex, pageSize);
}
/// <summary>
/// 失败返回
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="errorMsg"></param>
/// <param name="errCode"></param>
/// <returns></returns>
protected virtual ApiResult<T> Fail<T>(T? data, string errorMsg, int errCode) where T : class, new()
{
return ApiResult<T>.FailResult(errorMsg, errCode);
}
/// <summary>
/// 失败返回(分页)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="errorMsg"></param>
/// <param name="errCode"></param>
/// <returns></returns>
protected virtual PagedApiResult<T>? PagedFail<T>(T? data, string errorMsg, int errCode) where T : class, new()
{
return PagedApiResult<T>.FailResult(errorMsg, errCode);
}
}

View File

@ -6,5 +6,9 @@ namespace LFlow.Base.Interfaces;
/// </summary>
public interface IModule
{
/// <summary>
/// 配置模块
/// </summary>
/// <param name="services"></param>
void ConfigureModule(IServiceCollection services);
}

View File

@ -23,6 +23,7 @@ public interface IRepo<T, K> where T : IDataModel
/// 保存与更新
/// </summary>
/// <param name="entity"></param>
/// <param name="isUpdate"></param>
/// <returns></returns>
T SaveOrUpdate(T entity, bool isUpdate);
/// <summary>
@ -30,6 +31,7 @@ public interface IRepo<T, K> where T : IDataModel
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="pageTotal"></param>
/// <returns></returns>
List<T> GetAll(int pageIndex, int pageSize, ref int pageTotal);
@ -44,6 +46,6 @@ public interface IRepo<T, K> where T : IDataModel
/// </summary>
/// <param name="whereObj"></param>
/// <returns></returns>
List<K> WhereSearchId(T whereObj);
List<string> WhereSearchId(T whereObj);
}

View File

@ -1,17 +1,8 @@
using LFlow.Base.Utils;
namespace LFlow.Base.Interfaces;
/// <summary>
/// ·þÎñ½Ó¿Ú
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IService<T> where T : class, IModel, new()
/// 服务接口
/// </summary>
public interface IService//<T> where T : class, IModel, new()
{
T GetById(string id);
List<T> Search(T whereObj);
int DeleteById(string id);
T Save(T entity, bool isUpdate);
PagedApiResult<List<T>> GetAll(int pageIndex, int pageSize);
}

Binary file not shown.

View File

@ -8,19 +8,34 @@
<OutputPath>../LFlow_Bin/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>Base.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Serilog" Version="4.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Mapster" Version="7.4.1-pre01" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.2.24473.5" />
<PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="Serilog" Version="4.1.1-dev-02318" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.1-dev-10398" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="SQLite" Version="3.13.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.8.1" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.171-preview03" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Cache\LFlow.Cache.csproj" />
<ProjectReference Include="..\LFlow.InternalEventBus\LFlow.InternalEventBus.csproj" />
<ProjectReference Include="..\LFlow.Middleware\LFlow.Middleware.csproj" />
</ItemGroup>
</Project>

View File

@ -1,16 +1,35 @@
using System.Reflection;
using LFlow.Base.Interfaces;
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache;
using LFlow.InternalEventBus;
using Minio;
using LFlow.Middleware;
using LFlow.Middleware.Register;
using Serilog;
using Serilog.Events;
using SqlSugar;
using System.Reflection;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using System.Runtime;
using Microsoft.Extensions.Configuration;
namespace LFlow.Base;
/// <summary>
///
/// </summary>
public static class Program
{
/// <summary>
/// 入口
/// </summary>
/// <param name="args"></param>
///
public static void Main(string[] args)
{
// 先用Serilog的BootstrapLogger
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
@ -18,6 +37,7 @@ public static class Program
.WriteTo.Console()
.CreateBootstrapLogger();
ConfigDllLoader();
var builder = WebApplication.CreateBuilder(args);
// 添加到Service中
builder.Services.AddSerilog((services, lc) => lc
@ -26,21 +46,49 @@ public static class Program
.Enrich.FromLogContext()
// .WriteTo.Console()
);
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddSerilog(Log.Logger, dispose: true);
});
builder.Services.AddResponseCompression();
builder.Services.AddResponseCaching();
Log.Logger.Information("LoadSubService");
builder.Services.LoadSubService();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpContextAccessor();
builder.Services.AddResponseCompression();
Log.Logger.Information("ConfigureSqlSugar");
builder.Services.ConfigureSqlSugar();
builder.Services.AddControllers();
builder.Services.AddControllers(c =>
{
c.Conventions.Add(new ApiExplorerGroupPerVersionConvention());
});
//注入IMinIo
builder.Services.AddSingleton<IMinioClient>
(s =>
{
var config = builder.Configuration.GetSection("MinIoConfig");
IMinioClient client = new MinioClient()
.WithEndpoint(config.GetSection("endPoint").Value)
.WithCredentials(config.GetSection("AccessKey").Value, config.GetSection("SecretKey").Value)
.WithSSL(false)
.Build();
return client;
}
);
builder.Services.AddSwaggerGen(u =>
{
u.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "Ver.1",
@ -52,15 +100,45 @@ public static class Program
Email = "noemail@ling.chat"
}
});
u.IncludeXmlComments("Base.xml", true);
// 获取自定义的xml目录
var xmlPath = Path.Combine(System.AppContext.BaseDirectory, "Services");
DirectoryInfo dir = new(xmlPath);
// 获取目录下的所有xml文件并设置为swagger文档包含文件
dir.GetFiles("*.xml").ToList().ForEach(f =>
{
u.IncludeXmlComments(f.FullName, true);
u.SwaggerDoc(Path.GetFileNameWithoutExtension(f.Name), new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "Ver.1",
Title = Path.GetFileNameWithoutExtension(f.Name),
Description = "LFlow api test and document",
});
});
});
// 使用Serilog此处为了修复 `The logger is already frozen` 的问题重新配置Serilog
builder.Host.UseSerilog((context, services, config) =>
// 配置跨域策略
builder.Services.AddCors(options =>
{
config.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext();
// .WriteTo.Console();
}, true);
options.AddPolicy("AllowAny", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
builder.Host.UseSerilog(Log.Logger);
// 注册中间件
builder.Services.RegisterMiddlewares(App.SubServiceAssembly);
// 注册内部事件总线
builder.Services.AddInternalEventBus(App.SubServiceAssembly);
// 注册自身缓存器
builder.Services.AddSelfCache();
// init App.service
App.services = builder.Services;
var app = builder.Build();
@ -70,13 +148,26 @@ public static class Program
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Information;
});
app.MapControllers();
// app.MapGet("/", () => "Hello World!");
// 添加根目录映射
app.MapGet("/", async context =>
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath, "index.html"));
});
// if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(u =>
{
u.DocumentTitle = "LFlow";
var xmlPath = Path.Combine(System.AppContext.BaseDirectory, "Services");
DirectoryInfo dir = new(xmlPath);
// 获取目录下的所有xml文件并设置为swagger文档包含文件
dir.GetFiles("*.xml").ToList().ForEach(f =>
{
var progName = Path.GetFileNameWithoutExtension(f.Name);
u.SwaggerEndpoint($"/swagger/{progName}/swagger.json", progName);
});
});
}
// 在启动后调用Sqlsugar进行CodeFirst
@ -85,10 +176,28 @@ public static class Program
{
Log.Logger.Information("ApplicationStarted");
CodeFirst.InitTable();
InternalEventBus.InternalEventBus.Publish("Init", "ApplicationStarted");
});
// 启用自定义中间件
app.UseLFlowMiddleware();
// 启用内部事件总线
app.UseInternalEventBus();
// 启用缓存
app.UseResponseCaching();
// 启用压缩
app.UseResponseCompression();
// 启用静态文件支持
app.UseStaticFiles();
app.UseCors("AllowAny");
app.Run();
}
/// <summary>
/// 配置SqlSugar
/// </summary>
/// <param name="services"></param>
public static void ConfigureSqlSugar(this IServiceCollection services)
{
services.AddSingleton<ISqlSugarClient>(s =>
@ -98,6 +207,7 @@ public static class Program
DbType = SqlSugar.DbType.Sqlite,
ConnectionString = "DataSource=LFlow-dev.db",
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute //从实体特性中读取主键自增列信息
},
db =>
{
@ -106,9 +216,10 @@ public static class Program
{
//获取作IOC作用域对象
var appServive = s.GetService<IHttpContextAccessor>();
var log = s.GetService<Serilog.ILogger>();
// var obj = appServive?.HttpContext?.RequestServices.GetService<Log>();
// Console.WriteLine("AOP" + obj.GetHashCode());
Console.WriteLine($"{appServive?.HttpContext?.Request.Path}:{sql}\r\n{pars}");
log?.Debug($"{appServive?.HttpContext?.Request.Path}:{sql}\r\n{pars}");
};
});
sqlSugar.DbMaintenance.CreateDatabase();
@ -121,6 +232,7 @@ public static class Program
/// </summary>
public static void LoadSubService(this IServiceCollection services)
{
//TODO: 从配置文件中读取加载路径
var path = Path.Combine(AppContext.BaseDirectory, "Services");
// Console.WriteLine(path);
var files = Directory.GetFiles(path, "*.dll");
@ -128,40 +240,91 @@ public static class Program
{
foreach (var file in files)
{
Log.Logger.Information($"Load file -> {file}...");
var assembly = Assembly.LoadFile(file);
var types = assembly.GetTypes();
// bool isUseController = false;
foreach (var type in types)
try
{
// Console.WriteLine(type);
if (type.IsClass && !type.IsAbstract)
bool isModule = false;
Log.Logger.Information($"Load file -> {file}...");
var assembly = Assembly.LoadFile(file);
var types = assembly.GetTypes();
// bool isUseController = false;
foreach (var type in types)
{
var interfaces = type.GetInterfaces();
if (interfaces.Contains(typeof(IModule)))
// Console.WriteLine(type);
if (type.IsClass && !type.IsAbstract)
{
Log.Logger.Information($"\tFound IModule -> {type.FullName}");
var module = Activator.CreateInstance(type) as IModule;
module?.ConfigureModule(services);
var interfaces = type.GetInterfaces();
if (interfaces.Contains(typeof(IModule)))
{
Log.Logger.Information($"\tFound IModule -> {type.FullName}");
var module = Activator.CreateInstance(type) as IModule;
module?.ConfigureModule(services);
isModule = true;
}
}
}
// if (isUseController)
// {
// Log.Logger.Information($"\tAdd Controllers for {assembly.FullName}");
// // 添加Controller
// services.AddControllers().AddApplicationPart(assembly);
// }
if (isModule)
{
App.SubServiceAssembly.Add(assembly);
}
Log.Logger.Information("done.\r\n");
}
// if (isUseController)
// {
// Log.Logger.Information($"\tAdd Controllers for {assembly.FullName}");
// // 添加Controller
// services.AddControllers().AddApplicationPart(assembly);
// }
Log.Logger.Information("done.\r\n");
catch (Exception ex)
{
Log.Logger.Error(ex, $"Load file -> {file} error.");
}
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
/// <summary>
/// 配置DLL加载器
/// 接管DLL加载过程处理依赖问题从当前路径和Service文件夹中加载DLL
/// </summary>
public static void ConfigDllLoader()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var assemblyName = new AssemblyName(args.Name);
}
// 判断是否已经被加载 避免重复加载
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.FullName == assemblyName.FullName);
if (loadedAssembly != null)
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded.");
return loadedAssembly;
}
// 从当前路径下加载
var path = Path.Combine(AppContext.BaseDirectory, $"{assemblyName.Name}.dll");
if (File.Exists(path))
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}.");
return Assembly.LoadFile(path);
}
// 从Service文件夹中加载
path = Path.Combine(AppContext.BaseDirectory, "Service", $"{assemblyName.Name}.dll");
if (File.Exists(path))
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}.");
return Assembly.LoadFile(path);
}
Log.Logger?.Error($"Assembly {assemblyName.Name} not found.");
return null;
};
}
}

View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Serilog;
namespace LFlow.Base.Utils
{
/// <summary>
/// 控制器模型约定
/// </summary>
public class ApiExplorerGroupPerVersionConvention : IControllerModelConvention
{
/// <summary>
/// 配置
/// </summary>
/// <param name="controller"></param>
public void Apply(ControllerModel controller)
{
foreach (var action in controller.Actions)
{
action.ApiExplorer.GroupName = controller.ControllerName;
action.ApiExplorer.IsVisible = true;
Log.Logger.Information($"ApiExplorerGroup added -> {controller.ControllerName} / {action.ActionName}");
}
}
}
}

View File

@ -7,29 +7,73 @@
[Serializable]
public class ApiResult<T> where T : class, new()
{
/// <summary>
/// 数据
/// </summary>
public T? Data
{
get; set;
}
/// <summary>
/// 构造函数
/// </summary>
public ApiResult()
{
}
public ApiResult(bool success, string message, T data)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="success"></param>
/// <param name="message"></param>
/// <param name="code"></param>
/// <param name="data"></param>
public ApiResult(bool success, string message, int code, T? data)
{
Success = success;
Message = message;
Code = code;
Data = data;
}
/// <summary>
/// 是否成功
/// </summary>
public bool Success
{
get; set;
}
/// <summary>
/// 消息
/// </summary>
public string? Message
{
get; set;
}
/// <summary>
/// 状态码
/// </summary>
public int? Code
{
get; set;
}
/// <summary>
/// 成功返回
/// </summary>
/// <param name="data"></param>
/// <param name="message"></param>
/// <param name="code"></param>
/// <returns></returns>
public static ApiResult<T> SuccessResult(T? data, string message = "操作成功", int code = 200)
{
return new ApiResult<T>(true, message, code, data);
}
/// <summary>
/// 失败返回
/// </summary>
/// <param name="message"></param>
/// <param name="code"></param>
/// <returns></returns>
public static ApiResult<T> FailResult(string message = "操作失败", int code = 500)
{
return new ApiResult<T>(false, message, code, default);
}
}

View File

@ -0,0 +1,135 @@
using Minio.DataModel.Args;
using Minio.DataModel.Result;
using Minio.Exceptions;
using Minio;
namespace LFlow.Base.Utils
{
public class BucketManager
{
public static IMinioClient minioClient;
public BucketManager(IMinioClient IminioClient) {
minioClient= IminioClient;
}
/// <summary>
/// 1、判断bucket是否存在
/// </summary>
/// <param name="bucketName"></param>
/// <returns></returns>
public static async Task<string> IsExistStr(string bucketName)
{
try
{
BucketExistsArgs args = new BucketExistsArgs().WithBucket(bucketName);
bool found = await minioClient.BucketExistsAsync(args).ConfigureAwait(false);
Console.WriteLine("found。。。。。。", found);
if (found)
{
Console.WriteLine($"{bucketName}桶已存在");
return $"{bucketName}桶已存在";
}
else
{
Console.WriteLine($"{bucketName}桶不存在");
return $"{bucketName}桶不存在";
}
}
catch (MinioException e)
{
Console.WriteLine("[Bucket] Exception: {0}", e);
return "出错啦!!!";
}
}
/// <summary>
/// 2、创建一个bucket
/// </summary>
/// <param name="bucketName"></param>
/// <returns></returns>
public static async Task<string> Create(string? bucketName)
{
try
{
BucketExistsArgs args = new BucketExistsArgs().WithBucket(bucketName);
bool found = await minioClient.BucketExistsAsync(args).ConfigureAwait(false);
if (found)
{
return $"{bucketName}桶已存在";
}
else
{
MakeBucketArgs makeBucketArgs = new MakeBucketArgs().WithBucket(bucketName);
await minioClient.MakeBucketAsync(makeBucketArgs).ConfigureAwait(false);
return $"{bucketName}桶已成功创建";
}
}
catch (MinioException e)
{
Console.WriteLine("[Bucket] Exception: {0}", e);
return "出错啦!!!";
}
}
/// <summary>
/// 3、移除一个bucket
/// </summary>
/// <param name="bucketName"></param>
/// <returns></returns>
public static async Task<string> Delete(string? bucketName)
{
try
{
BucketExistsArgs args = new BucketExistsArgs().WithBucket(bucketName);
bool found = await minioClient.BucketExistsAsync(args).ConfigureAwait(false);
if (!found)
{
return $"{bucketName}桶不存在";
}
else
{
RemoveBucketArgs removeBucketArgs = new RemoveBucketArgs().WithBucket(bucketName);
await minioClient.RemoveBucketAsync(removeBucketArgs);
return $"{bucketName}桶删除成功";
}
}
catch (MinioException e)
{
Console.WriteLine("[Bucket] Exception: {0}", e);
return "出错啦!!!";
}
}
/// <summary>
/// 4、获取已有的bucket列表
/// </summary>
/// <returns></returns>
public static async Task<ListAllMyBucketsResult?> GetList()
{
try
{
return await minioClient.ListBucketsAsync();
}
catch (MinioException e)
{
Console.WriteLine("Error occurred: " + e);
return null;
}
}
}
}

View File

@ -1,19 +1,28 @@
using System;
using System.Collections.Concurrent;
using System.Net;
using Serilog;
using SqlSugar;
using System.Collections.Concurrent;
namespace LFlow.Base.Utils;
/// <summary>
/// CodeFirst
/// </summary>
public static class CodeFirst
{
// 线程安全的类型列表
private static readonly ConcurrentBag<Type> _types = new();
/// <summary>
/// 类型集合
/// </summary>
private static readonly ConcurrentBag<Type> _types = [];
/// <summary>
/// 添加类型
/// </summary>
/// <param name="type"></param>
public static void AddType(Type type)
{
_types.Add(type);
}
/// <summary>
/// 初始化表
/// </summary>
internal static void InitTable()
{
var logger = App.GetService<Serilog.ILogger>()!;
@ -25,6 +34,9 @@ public static class CodeFirst
}
}
/// <summary>
/// 初始化数据库种子
/// </summary>
internal static void InitDBSeed()
{
//TODO: Seed data

View File

@ -1,9 +1,11 @@
using System;
using LFlow.Base.Interfaces;
using Mapster;
namespace LFlow.Base.Utils;
public class Mapper
/// <summary>
/// 映射器
/// </summary>
public static class Mapper
{
/// <summary>
/// 将一个对象映射到另一个对象
@ -19,4 +21,18 @@ public class Mapper
}
return source.Adapt<T>();
}
/// <summary>
/// 将一个数据模型映射到另一个对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public static T? MapTo<T>(this IDataModel model)
{
if (model == null)
{
return default;
}
return model.Adapt<T>();
}
}

View File

@ -0,0 +1,171 @@
using Microsoft.VisualBasic.FileIO;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Minio.Exceptions;
using System;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace LFlow.Base.Utils
{
public class MinIOService
{
private readonly IMinioClient minioClient;
private readonly BucketManager bucketManager;
private static string bucketName = "testbucket";
public MinIOService(IMinioClient _minioClient)
{
minioClient = _minioClient;
bucketManager = new BucketManager(_minioClient);
}
public async Task<string> Upload(IFormFile file)
{
try
{
//logger.LogInformation("文件开始上传");
string uploadsPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "uploads");
if (!Directory.Exists(uploadsPath))
{
Directory.CreateDirectory(uploadsPath);
}
string fileName = file.FileName;
// string filePath = Path.Combine(uploadsPath, fileName);
// string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
// string fileExtension = Path.GetExtension(fileName);
// string newFileName = null;
// int counter = 1;
// while (File.Exists(filePath))
// {
// //重新命名文件
// string Name = $"{fileNameWithoutExtension}_{counter}{fileExtension}";
// filePath = Path.Combine(uploadsPath, Name);
// newFileName = Path.GetFileNameWithoutExtension(filePath);
// counter++;
// }
// if (newFileName == null)
// {
// newFileName = Path.GetFileNameWithoutExtension(fileName);
// }
// using (var stream = new FileStream(filePath, FileMode.Create))
// {
// file.CopyTo(stream);
// }
//AddFileInfo(filePath, Path.GetFileNameWithoutExtension(fileName), newFileName);
var beArgs = new BucketExistsArgs()
.WithBucket(bucketName);
bool found = await minioClient.BucketExistsAsync(beArgs);
if (!found)
{
var mbArgs = new MakeBucketArgs()
.WithBucket(bucketName);
await minioClient.MakeBucketAsync(mbArgs).ConfigureAwait(false);
}
// 设置上传文件的对象名
var objectName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
var putObjectArgs = new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
// .WithFileName(filePath)
.WithStreamData(file.OpenReadStream())
.WithObjectSize(file.Length)
.WithContentType(file.ContentType);
var response = await minioClient.PutObjectAsync(putObjectArgs);
Console.WriteLine($"文件 '{fileName}' 上传到 bucket '{bucketName}' 中,文件名为 '{objectName}'。");
return objectName;
}
catch (Exception ex)
{
// 处理异常
throw new ApplicationException("文件上传失败", ex);
}
}
/// <summary>
/// 获取桶列表
/// </summary>
/// <returns></returns>
public async Task<List<string>> GetBuckets()
{
var list = await minioClient.ListBucketsAsync().ConfigureAwait(false);
var buckets = list.Buckets.Select(b => b.Name).ToList();
return buckets;
}
/// <summary>
/// 下载接口
/// </summary>
/// <returns></returns>
public async Task DownLoad()
{
try
{
StatObjectArgs statObjectArgs = new StatObjectArgs()
.WithBucket(bucketName)
.WithObject("替换你上传到minio服务器中生成的objectName");
await minioClient.StatObjectAsync(statObjectArgs);
GetObjectArgs getObjectArgs = new GetObjectArgs()
.WithBucket(bucketName)
.WithObject("替换你上传到minio服务器中生成的objectName")
.WithFile("D:\\photo.jpg"); //替换你实际的文件输出路径
await minioClient.GetObjectAsync(getObjectArgs);
}
catch (MinioException e)
{
Console.Out.WriteLine("Error occurred:" + e);
}
}
/// <summary>
/// 后缀名枚举
/// </summary>
/// <param name="fileExtension"></param>
/// <returns></returns>
//public string DetermineFileType(string fileExtension)
//{
// HashSet<string> supportedImageExtensions = new HashSet<string>
// {
// ".jpg",".jpeg", ".png",".gif",".bmp",".tiff",".tif",".svg",".webp", ".ico"
// };
// HashSet<string> supportedVideoExtensions = new HashSet<string>
// {
// ".mp4",".avi",".mkv",".mov",".wmv",".flv",".webm"
// };
// HashSet<string> supportedDocumentExtensions = new HashSet<string>
// {
// ".doc",".docx",".pdf",".txt",".ppt",".pptx",".xls",".xlsx",
// };
// if (supportedDocumentExtensions.Contains(fileExtension.ToLower()))
// {
// return FileType.document.ToString();
// }
// else if (supportedImageExtensions.Contains(fileExtension.ToLower()))
// {
// return FileType.image.ToString();
// }
// else if (supportedVideoExtensions.Contains(fileExtension.ToLower()))
// {
// return FileType.video.ToString();
// }
// else
// {
// return FileType.other.ToString();
// }
}
}

View File

@ -1,7 +1,9 @@
using LFlow.Base.Interfaces;
namespace LFlow.Base.Utils;
/// <summary>
/// 对象转换为where条件
/// </summary>
public static class ObjectToWhereExp
{
/// <summary>
@ -24,7 +26,7 @@ public static class ObjectToWhereExp
}
else if (property.PropertyType == typeof(DateTime) && value is DateTime dateTime)
{
if (dateTime != default(DateTime))
if (dateTime != default)
{
whereExp += $" and {property.Name} like '%{value}%'";
}

View File

@ -7,37 +7,83 @@
[Serializable]
public class PagedApiResult<T> : ApiResult<T> where T : class, new()
{
/// <summary>
/// 数据
/// </summary>
public new T? Data
{
get; set;
}
/// <summary>
/// 总行数
/// </summary>
public int TotalCount
{
get; set;
}
/// <summary>
/// 当前页
/// </summary>
public int PageIndex
{
get; set;
}
/// <summary>
/// 每页行数
/// </summary>
public int PageSize
{
get; set;
}
/// <summary>
/// 构造函数
/// </summary>
public PagedApiResult()
{
}
public PagedApiResult(bool success, string message, T data, int totalCount, int pageIndex, int pageSize)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="success"></param>
/// <param name="message"></param>
/// <param name="code"></param>
/// <param name="data"></param>
/// <param name="totalCount"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
public PagedApiResult(bool success, string message, int code, T? data, int totalCount, int pageIndex, int pageSize)
{
Success = success;
Message = message;
Code = code;
Data = data;
TotalCount = totalCount;
PageIndex = pageIndex;
PageSize = pageSize;
}
/// <summary>
/// 成功返回
/// </summary>
/// <param name="data"></param>
/// <param name="totalCount"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="message"></param>
/// <param name="code"></param>
/// <returns></returns>
public static PagedApiResult<T> SuccessResult(T? data, int totalCount, int pageIndex, int pageSize, string message = "操作成功", int code = 200)
{
return new PagedApiResult<T>(true, message, code, data, totalCount, pageIndex, pageSize);
}
/// <summary>
/// 失败返回
/// </summary>
/// <param name="message"></param>
/// <param name="errCode"></param>
/// <returns></returns>
public static new PagedApiResult<T>? FailResult(string message = "操作失败", int errCode = 500)
{
return new PagedApiResult<T>(false, message, errCode, null, 0, 0, 0);
}
}

View File

@ -1,5 +1,7 @@
namespace LFlow.Base.Utils;
/// <summary>
/// 注册模块
/// </summary>
public static class RegisterModule
{
/// <summary>

View File

@ -1,21 +1,28 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.AspNetCore.Mvc": "Debug",
"Microsoft.AspNetCore.Routing": "Debug",
"Microsoft.AspNetCore.Hosting": "Debug"
}
"Urls": "http://*:8088;http://*:3453",
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore.Mvc": "Debug",
"Microsoft.AspNetCore.Routing": "Debug",
"Microsoft.AspNetCore.Hosting": "Debug"
}
},
"WriteTo": [
{
"Name": "Console"
}
]
},
"WriteTo": [
{
"Name": "Console"
}
]
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=192.168.3.85;Initial Catalog=PluginAdmin-dev;User Id=sa;Password=Sa1234;Encrypt=True;TrustServerCertificate=True"
}
"MinIoConfig": {
"EndPoint": "192.168.3.85:9000",
"AccessKey": "minioadmin",
"SecretKey": "minioadmin",
"UseSSL": true
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=192.168.3.85;Initial Catalog=PluginAdmin-dev;User Id=sa;Password=Sa1234;Encrypt=True;TrustServerCertificate=True"
}
}

View File

@ -0,0 +1,55 @@
using LFlow.Cache.Interface;
using Microsoft.Extensions.Caching.Memory;
namespace LFlow.Cache.Cacher
{
/// <summary>
/// 自身的单实例缓存
/// </summary>
public class SelfSingleCache : ISelfCache
{
protected MemoryCache _cache = new(new MemoryCacheOptions()
{
ExpirationScanFrequency = new TimeSpan(0, 0, 10)
});
public MemoryCache CacheProvider => _cache;
public Task ClearAsync()
{
CacheProvider.Clear();
return Task.CompletedTask;
}
public Task<T?> GetAsync<T>(string key)
{
return Task.FromResult(CacheProvider.Get<T>(key));
}
public Task<object?> GetAsync(string key)
{
return Task.FromResult(CacheProvider.Get(key));
}
public Task RemoveAsync(string key)
{
if (CacheProvider.TryGetValue(key, out _))
CacheProvider.Remove(key);
return Task.CompletedTask;
}
public Task SetAsync<T>(string key, T data)
{
CacheProvider.Set(key, data);
return Task.CompletedTask;
}
public Task SetAsync<T>(string key, T data, TimeSpan expiredTime)
{
CacheProvider.Set(key, data, expiredTime);
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,59 @@
using Microsoft.Extensions.Caching.Memory;
namespace LFlow.Cache.Interface
{
/// <summary>
/// 自身缓存接口
/// 后续扩展,可分为内部缓存和外部缓存
/// ISelfCache 只作为内部缓存接口
/// 内外部缓存的区别在于,内部缓存是程序自身的缓存,外部缓存则可共享给其他系统,并暴露在外
/// </summary>
public interface ISelfCache
{
/// <summary>
/// 缓存提供者目前只支持MemoryCache后续可扩展为使用接口然后实现不同的缓存提供者
/// </summary>
protected MemoryCache CacheProvider { get; }
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="key">键</param>
/// <returns></returns>
Task<T?> GetAsync<T>(string key);
/// <summary>
/// 获取
/// </summary>
/// <param name="key">键</param>
/// <returns></returns>
Task<object?> GetAsync(string key);
/// <summary>
/// 存入一个值
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="key">键</param>
/// <param name="data"></param>
/// <returns></returns>
Task SetAsync<T>(string key, T data);
/// <summary>
/// 存入一个值
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="key">键</param>
/// <param name="data">数据</param>
/// <param name="expiredTime">过期时间</param>
/// <returns></returns>
Task SetAsync<T>(string key, T data, TimeSpan expiredTime);
/// <summary>
/// 移除一个键与他的值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Task RemoveAsync(string key);
/// <summary>
/// 清空缓存
/// </summary>
/// <returns></returns>
Task ClearAsync();
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0-rc.2.24473.5" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
using LFlow.Cache.Cacher;
using LFlow.Cache.Interface;
using Microsoft.Extensions.DependencyInjection;
namespace LFlow.Cache
{
public static class MemoryCacheExtensions
{
public static void AddSelfCache(this IServiceCollection services)
{
services.AddSingleton<ISelfCache, SelfSingleCache>();
}
}
}

View File

@ -0,0 +1,15 @@
namespace LFlow.InternalEventBus.Interface
{
public interface IInternalEventSubscriber
{
int Priority { get; }
string EventName { get; }
/// <summary>
/// 事件处理,返回是否处理成功
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> Handle(object data);
}
}

View File

@ -0,0 +1,68 @@
using LFlow.InternalEventBus.Interface;
using System.Collections.Concurrent;
namespace LFlow.InternalEventBus
{
public class InternalEventBus
{
private static readonly ConcurrentDictionary<string, List<IInternalEventSubscriber>> _eventSubscriberDict = new();
/// <summary>
/// 注册事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="subscriber"></param>
public static void Register(string eventName, IInternalEventSubscriber subscriber)
{
if (!_eventSubscriberDict.ContainsKey(eventName))
{
_eventSubscriberDict.TryAdd(eventName, []);
}
_eventSubscriberDict[eventName].Add(subscriber);
}
/// <summary>
/// 取消注册事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="subscriber"></param>
public static void UnRegister(string eventName, IInternalEventSubscriber subscriber)
{
if (_eventSubscriberDict.ContainsKey(eventName))
{
_eventSubscriberDict[eventName].Remove(subscriber);
}
}
/// <summary>
/// 发布事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="data"></param>
/// <returns></returns>
public static async void Publish(string eventName, object data)
{
if (_eventSubscriberDict.ContainsKey(eventName))
{
foreach (var subscriber in _eventSubscriberDict[eventName].OrderBy(i => i.Priority))
{
var isContinue = await subscriber.Handle(data);
if (!isContinue)
{
return;
}
}
}
}
/// <summary>
/// 发布事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="data"></param>
/// <returns></returns>
public static async Task PublishAsync(string eventName, object data)
{
await Task.Factory.StartNew(() => Publish(eventName, data));
}
}
}

View File

@ -0,0 +1,66 @@
using LFlow.InternalEventBus.Interface;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using System.Reflection;
namespace LFlow.InternalEventBus
{
public static class InternalEventBusExtensions
{
private static readonly ConcurrentDictionary<Type, IInternalEventSubscriber> _eventSubscribers = new();
private static readonly ConcurrentBag<Type> _eventSubscriberTypes = [];
/// <summary>
/// 添加事件订阅服务
/// </summary>
/// <param name="services"></param>
/// <param name="assemblies"></param>
public static void AddInternalEventBus(this IServiceCollection services, List<Assembly> assemblies)
{
assemblies?.ForEach(assembly =>
{
var types = assembly.GetTypes().ToList();
foreach (var type in types)
{
if (type.GetInterface(nameof(IInternalEventSubscriber)) != null)
{
services.AddSingleton(type);
_eventSubscriberTypes.Add(type);
}
}
});
}
/// <summary>
/// 构建事件对象列表
/// </summary>
/// <param name="app"></param>
private static void BuildInternalEventBusServiceProvider(this IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
_eventSubscriberTypes.ToList().ForEach(type =>
{
if (serviceProvider.GetService(type) is IInternalEventSubscriber subscriber)
_eventSubscribers.TryAdd(type, subscriber);
});
}
/// <summary>
/// 启用内部事件
/// </summary>
/// <param name="app"></param>
public static void UseInternalEventBus(this IApplicationBuilder app)
{
// build serviceProvider and eventSubscribers
app.BuildInternalEventBusServiceProvider();
// sort by priority
var orderedSubscribers = _eventSubscribers.Values.OrderBy(t => t?.Priority ?? 0).ToList();
orderedSubscribers.ForEach(subscriber =>
{
if (subscriber is IInternalEventSubscriber eventSubscriber)
{
InternalEventBus.Register(eventSubscriber.EventName, eventSubscriber);
}
});
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Folder Include="Enum\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
namespace LFlow.Middleware
{
public interface ILFlowMiddleware
{
/// <summary>
/// 优先级
/// </summary>
int Priority { get; }
/// <summary>
/// 中间件执行入口
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
Task RunAsync(HttpContext context, Func<Task> next);
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<OutputType>Library</OutputType>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,19 @@
using LFlow.Middleware.Register;
using Microsoft.AspNetCore.Builder;
namespace LFlow.Middleware
{
public static class MiddlewareExtensions
{
/// <summary>
/// Use LFlow Middleware
/// 配置中间件
/// </summary>
/// <param name="app"></param>
public static async void UseLFlowMiddleware(this IApplicationBuilder app)
{
app.BuildMiddlewareServiceProvider();
await MiddlewareRegister.Handle(app);
}
}
}

View File

@ -0,0 +1,48 @@
namespace LFlow.Middleware.Middlewares
{
public class LoggingMiddleware : ILFlowMiddleware
{
private readonly ILogger<LoggingMiddleware> _logger;
public LoggingMiddleware(ILogger<LoggingMiddleware> logger)
{
_logger = logger;
}
public int Priority => 1;
public async Task RunAsync(HttpContext context, Func<Task> next)
{
var controllerName = context.GetRouteData()?.Values["controller"]?.ToString();
var actionName = context.GetRouteData()?.Values["action"]?.ToString();
var requestId = context.Request.Headers["X-Request-Id"].ToString();
if (string.IsNullOrEmpty(requestId))
{
requestId = Guid.NewGuid().ToString();
context.Request.Headers.Append("X-Request-Id", requestId);
}
using (_logger?.BeginScope(requestId))
{
_logger?.LogInformation("Request Id: {requestId}", requestId);
_logger?.LogInformation("Controller: {controllerName} / Action : {actionName}", controllerName, actionName);
// _logger?.LogInformation("Router Data {@routerData}", context.GetRouteData());
try
{
await next();
}
catch (Exception ex)
{
_logger?.LogError(ex, "An error occurred while processing the request. Request Id: {requestId}", requestId);
//await context.Response.WriteAsync(JsonConvert.SerializeObject(ApiResult<object>.FailResult("无权限!", 100501)));
// _logger?.LogInformation("Response {@response}", context.Response);
}
}
if (!context.Response.Headers.ContainsKey("X-Request-Id"))
{
}
// context.Response.Headers.Append("X-Request-Id", requestId);
}
}
}

View File

@ -0,0 +1,64 @@
using System.Reflection;
namespace LFlow.Middleware.Register
{
public static class MiddlewareRegister
{
private static IServiceProvider? _serviceProvider;
private static readonly IDictionary<Type, ILFlowMiddleware> _middlewares = new Dictionary<Type, ILFlowMiddleware>();
private static List<Type> _middlewareTypes = [];
public static void RegisterMiddlewares(this IServiceCollection service, List<Assembly> assemblies)
{
if (assemblies == null || assemblies.Count == 0)
{
return;
}
// 列入系统内置中间件
var allAssemblies = new List<Assembly>(assemblies)
{
Assembly.GetAssembly(typeof(ILFlowMiddleware))!
};
allAssemblies.ForEach(assembly =>
{
var types = assembly.GetTypes().ToList();
foreach (var type in types)
{
if (type.GetInterface(nameof(ILFlowMiddleware)) != null)
{
service.AddSingleton(type);
_middlewareTypes.Add(type);
}
}
});
}
public static void BuildMiddlewareServiceProvider(this IApplicationBuilder application)
{
_serviceProvider = application.ApplicationServices;
_middlewareTypes.ForEach(type =>
{
if (_serviceProvider.GetService(type) is ILFlowMiddleware middleware)
_middlewares.Add(type, middleware);
});
}
/// <summary>
/// 处理中间件
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public static async Task Handle(IApplicationBuilder application)
{
//var logger = application.ApplicationServices.GetService<ILogger>();
var orderedMiddlewares = _middlewares.Values.OrderBy(m => m.Priority).ToList();
foreach (var middleware in orderedMiddlewares)
{
await Task.FromResult(application.Use(middleware.RunAsync));
//logger?.LogInformation("Middleware {MiddlewareName} is registered.", middleware.GetType().Name);
Console.WriteLine($"[{DateTime.Now:hh:mm:ss} INF] Middleware {middleware.GetType().Name} is registered.");
}
}
}
}

View File

@ -0,0 +1,43 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.OnlineManegement.Model;
using LFlow.OnlineManegement.Service;
using Microsoft.AspNetCore.Mvc;
namespace LFlow.OnlineManegement.Controller;
/// <summary>
/// 在线用户管理
/// </summary>
/// <param name="service"></param>
public class OnlineManagementController(IOnlineManagementService service) : BaseController
{
/// <summary>
/// 查询所有在线用户
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
[HttpGet]
public PagedApiResult<List<OnlineDto>> ListAll(int pageIndex, int pageSize)
{
int dataTotal = 0;
var result = service.GetAllOnlineUser(pageIndex, pageSize, ref dataTotal);
if (result == null || result.Count == 0)
{
return PagedFail(result, "No data found", 404);
}
return Success(result, dataTotal, pageIndex, pageSize);
}
/// <summary>
/// 注册在线用户
/// </summary>
/// <param name="onlineInfo"></param>
/// <returns></returns>
[HttpPost]
public ApiResult<OnlineDto> OnlineRegistered(OnlineDto onlineInfo)
{
return Success(service.OnlineRegistered(onlineInfo));
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>..\LFlow_Bin\Services\OnlineManegement.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using LFlow.Base.Interfaces;
namespace LFlow.OnlineManegement.Model
{
public class OnlineDto : IModel
{
public string? ID { get; set; }
public string? HostName { get; set; }
public string? IPAddress { get; set; }
public string? MacAddress { get; set; }
public string? OS { get; set; }
public string? LastOnlineTime { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.OnlineManegement.Model
{
[SugarTable("T_U_ONLINE")]
public class OnlineModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID { get; set; }
public string HostName { get; set; }
public string IPAddress { get; set; }
public string MacAddress { get; set; }
public string OS { get; set; }
public string LastOnlineTime { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.OnlineManegement.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.OnlineManegement
{
public class OnlineManegementModule : IModule
{
public void ConfigureModule(IServiceCollection services)
{
// 将模型添加到需要初始化数据库表的队列中
CodeFirst.AddType(typeof(OnlineModel));
// 获取当前程序集
var assembly = Assembly.GetAssembly(typeof(OnlineManegementModule))!;
var types = assembly.GetTypes().ToList();
//注册服务
RegisterModule.RegisterAllService(types, services);
//注册仓储
RegisterModule.RegisterAllRepo(types, services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("OnlineManegementModule ConfigureModule done");
}
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.OnlineManegement.Model;
using SqlSugar;
namespace LFlow.OnlineManegement.Repository
{
public class OnlineManagementRepo(ISqlSugarClient db) : DefaultCurdRepo<OnlineModel, string>(db)
{
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Interfaces;
using LFlow.OnlineManegement.Model;
namespace LFlow.OnlineManegement.Service;
public interface IOnlineManagementService : IService//<VersionDto>
{
List<OnlineDto> GetAllOnlineUser(int pageIndex, int pageSize, ref int dataTotal);
OnlineDto OnlineRegistered(OnlineDto onlineInfo);
}

View File

@ -0,0 +1,38 @@
using LFlow.Base.Interfaces;
using LFlow.OnlineManegement.Model;
using Mapster;
namespace LFlow.OnlineManegement.Service;
/// <summary>
/// 在线管理服务
/// </summary>
public class OnlineManagementService(IRepo<OnlineModel, string> _repo) : IOnlineManagementService
{
public List<OnlineDto> GetAllOnlineUser(int pageIndex, int pageSize, ref int dataTotal)
{
var result = _repo.GetAll(pageIndex, pageSize, ref dataTotal).Adapt<List<OnlineDto>>();
return result;
}
public OnlineDto OnlineRegistered(OnlineDto onlineInfo)
{
bool isUpdate = false;
if (string.IsNullOrEmpty(onlineInfo.ID))
{
onlineInfo.ID = Guid.NewGuid().ToString();
}
else
{
if (_repo.Get(onlineInfo.ID) == null)
{
isUpdate = false;
}
else
{
isUpdate = true;
}
}
return _repo.SaveOrUpdate(onlineInfo.Adapt<OnlineModel>(), isUpdate).Adapt<OnlineDto>();
}
}

View File

@ -0,0 +1,75 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Permission.Model;
using LFlow.Permission.Service;
using Microsoft.AspNetCore.Mvc;
using Serilog;
namespace LFlow.Permission.Controller
{
/// <summary>
/// 权限控制
/// </summary>
public class PermissionController : BaseController
{
/// <summary>
/// 权限服务
/// </summary>
private IPermissionService service;
/// <summary>
/// 日志
/// </summary>
private ILogger logger;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="service"></param>
/// <param name="logger"></param>
public PermissionController(IPermissionService service, ILogger logger)
{
this.service = service;
this.logger = logger;
}
/// <summary>
/// 查询权限
/// </summary>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
[HttpGet]
public async Task<PagedApiResult<List<PermissionDto>>> GetAll(int pageSize, int pageIndex)
{
var total = 0;
var list = await service.GetPermissionListAsync(pageIndex, pageSize, ref total);
return new PagedApiResult<List<PermissionDto>>
{
Data = list,
TotalCount = total
};
}
/// <summary>
/// 添加程序权限
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ApiResult<List<PermissionDto>>> GetProgPermissions()
{
var result = await service.GetPermissions();
return Success(result);
}
/// <summary>
/// Test method to throw an exception
/// </summary>
/// <returns>ApiResult with an error message</returns>
[HttpGet]
public Task<ApiResult<object>> TestException()
{
// var a = 0 / 1;
throw new Exception("Test exception");
// return Task.FromResult(Success((object)"Success"));
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<EnableDynamicLoading>true</EnableDynamicLoading>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>..\LFlow_Bin\Services\Permission.xml</DocumentationFile>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,64 @@
using LFlow.Base.Interfaces;
namespace LFlow.Permission.Model
{
public class PermissionDto : IModel
{
/// <summary>
/// ID
/// </summary>
public string? ID
{
get;
set;
}
/// <summary>
/// 角色ID
/// </summary>
public string? RoleId
{
get;
set;
}
/// <summary>
/// 权限对应程序ID
/// </summary>
public string? PermissionProgID
{
get;
set;
}
/// <summary>
/// 权限对应程序名称
/// </summary>
public string? PermissionProgName
{
get;
set;
}
/// <summary>
/// 权限名称
/// </summary>
public string? PermissionName
{
get;
set;
}
/// <summary>
/// 权限接口
/// </summary>
public string? PermissionAction
{
get;
set;
}
/// <summary>
/// 是否公开
/// </summary>
public bool IsPublic
{
get;
set;
}
}
}

View File

@ -0,0 +1,63 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.Permission.Model
{
[SugarTable("T_U_PERMISSION")]
public class PermissionModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID
{
get;
set;
}
public string RoleId
{
get;
set;
}
/// <summary>
/// 权限对应程序ID
/// </summary>
public string ProgID
{
get;
set;
}
/// <summary>
/// 权限对应程序名称
/// </summary>
public string ProgName
{
get;
set;
}
/// <summary>
/// 权限名称
/// </summary>
public string PermissionName
{
get;
set;
}
/// <summary>
/// 权限接口
/// </summary>
public string PermissionAction
{
get;
set;
}
/// <summary>
/// 是否公开
/// </summary>
public bool IsPublic
{
get;
set;
}
}
}

View File

@ -0,0 +1,62 @@
using System.Net.Sockets;
using LFlow.Base;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Middleware;
using LFlow.Permission.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Newtonsoft.Json;
namespace LFlow.Permission;
/// <summary>
/// 权限中间件
/// </summary>
public class PermissionMiddleware(ISelfCache selfCache ) : ILFlowMiddleware
{
/// <summary>
/// 优先级
/// </summary>
public int Priority => 1;
/// <summary>
/// 执行入口
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task RunAsync(HttpContext context, Func<Task> next)
{
// var progName = context.GetRouteData()?.Values["controller"]?.ToString();
// var progAction = context.GetRouteData()?.Values["action"]?.ToString();
// if (progName != null)
// {
// var service = App.GetService<IPermissionService>();
// var progPermission = service != null ? await service.GetProgPerminssionListAsync(progName) : null;
// var currentPermission = progPermission?.FirstOrDefault(p => p.PermissionAction == progAction);
// if (currentPermission == null || currentPermission!.IsPublic)
// {
// await next();
// }
// else
// {
// //TODO 从缓存中根据Token获取用户信息并判断是否有权限
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ApiResult<object>.FailResult("无权限!", 100501)));
// var token = context.Request.Cookies["Token"]?.ToString();
// if(token != null){
// var obj = selfCache.GetAsync(token!);
// }else{
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ApiResult<object>.FailResult("未登录!", 100500)));
// }
// }
// }
// // 预检请求
// if (context.Request.Method == "OPTIONS")
// {
await next();
// // context.Response.StatusCode = StatusCodes.Status200OK;
// }
}
}

View File

@ -0,0 +1,29 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Permission.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.Permission;
/// <summary>
/// 权限模块
/// </summary>
public class PermissionModule : IModule
{
/// <summary>
/// 配置模块
/// </summary>
/// <param name="services"></param>
public void ConfigureModule(IServiceCollection services)
{
CodeFirst.AddType(typeof(PermissionModel));
var assembly = Assembly.GetAssembly(typeof(PermissionModule))!;
var types = assembly.GetTypes().ToList();
types.RegisterAllService(services);
types.RegisterAllRepo(services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManagementModule ConfigureModule done");
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.Permission.Model;
using SqlSugar;
namespace LFlow.Permission.Repository
{
internal class RoleRepo(ISqlSugarClient client) : DefaultCurdRepo<PermissionModel, string>(client)
{
}
}

View File

@ -0,0 +1,57 @@
using LFlow.Base.Interfaces;
using LFlow.Permission.Model;
namespace LFlow.Permission.Service
{
public interface IPermissionService : IService//<VersionDto>
{
/// <summary>
/// 获取权限列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="total"></param>
/// <returns></returns>
Task<List<PermissionDto>> GetPermissionListAsync(int pageIndex, int pageSize, ref int total);
/// <summary>
/// 根据ID获取权限信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<PermissionDto> GetPermissionAsync(string id);
/// <summary>
/// 添加一项权限
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
Task<PermissionDto> AddPermissionAsync(PermissionDto model);
/// <summary>
/// 更新权限内容
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
Task<PermissionDto> UpdatePermissionAsync(PermissionDto model);
/// <summary>
/// 删除一项权限
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<int> DeletePermissionAsync(string id);
/// <summary>
/// 获取程序权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
Task<List<PermissionDto>> GetProgPerminssionListAsync(string progID);
/// <summary>
/// 获取所有权限项
/// </summary>
/// <returns></returns>
Task<List<PermissionDto>> GetPermissions();
/// <summary>
/// 获取用户权限
/// </summary>
/// <returns></returns>
Task<List<PermissionDto>> GetUserPermissions();
}
}

View File

@ -0,0 +1,140 @@
using LFlow.Base;
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Permission.Model;
using Mapster;
using Microsoft.AspNetCore.Mvc.Routing;
namespace LFlow.Permission.Service
{
/// <summary>
/// 角色服务
/// </summary>
public class PermissionService(IRepo<PermissionModel, string> repo, ISelfCache cacher) : IPermissionService
{
/// <summary>
/// 添加权限项
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<PermissionDto> AddPermissionAsync(PermissionDto model)
{
var savedModel = repo.SaveOrUpdate(model.Adapt<PermissionModel>(), isUpdate: false);
var result = savedModel?.MapTo<PermissionDto>() ?? throw new InvalidOperationException("Failed to add the permission model.");
return Task.FromResult(result);
}
/// <summary>
/// 删除权限项并清理缓存
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Task<int> DeletePermissionAsync(string id)
{
cacher.RemoveAsync(id);
return Task.FromResult(repo.DeleteById(id));
}
/// <summary>
/// 根据ID获取权限
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<PermissionDto> GetPermissionAsync(string id)
{
var cachedPermission = await cacher.GetAsync<PermissionModel>(id);
if (cachedPermission != null)
{
return cachedPermission!.MapTo<PermissionDto>()!;
}
var permission = await Task.FromResult(repo.Get(id));
if (permission != null)
{
await cacher.SetAsync(id, permission);
}
return permission?.MapTo<PermissionDto>() ?? new PermissionDto();
}
/// <summary>
/// 获取权限列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="total"></param>
/// <returns></returns>
public Task<List<PermissionDto>> GetPermissionListAsync(int pageIndex, int pageSize, ref int total)
{
return Task.FromResult(repo.GetAll(pageIndex, pageSize, ref total).Adapt<List<PermissionDto>>());
}
public Task<List<PermissionDto>> GetPermissions()
{
List<PermissionDto> permissions = [];
App.SubServiceAssembly.ForEach(service =>
{
var types = service.GetTypes();
foreach (var item in types.Where(type => type.IsSubclassOf(typeof(BaseController))))
{
var progName = item.Name.Replace("Controller", "");
foreach (var method in item.GetMethods())
{
var attrs = method.CustomAttributes.Where(attr =>
{
return attr.AttributeType.IsSubclassOf(typeof(HttpMethodAttribute));
});
if (attrs != null && attrs.Count() > 0)
{
var permission = new PermissionDto
{
//ProgID = progName,
PermissionAction = method.Name,
PermissionProgID = progName
};
permissions.Add(permission);
}
}
}
});
return Task.FromResult(permissions);
}
/// <summary>
/// 获取程序权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
public Task<List<PermissionDto>> GetProgPerminssionListAsync(string progID)
{
return Task.FromResult(repo.Search(new PermissionModel
{
ProgID = progID
}).Adapt<List<PermissionDto>>());
}
public Task<List<PermissionDto>> GetUserPermissions()
{
throw new NotImplementedException();
}
/// <summary>
/// 更新权限项
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<PermissionDto> UpdatePermissionAsync(PermissionDto model)
{
if (model != null && !string.IsNullOrEmpty(model.ID))
{
cacher.RemoveAsync(model.ID);
}
if (model != null)
{
var permissionModel = model?.Adapt<PermissionModel>() ?? throw new ArgumentNullException(nameof(model));
var savedModel = repo.SaveOrUpdate(permissionModel, isUpdate: true);
return Task.FromResult(savedModel?.MapTo<PermissionDto>() ?? throw new InvalidOperationException("Failed to update the permission model."));
}
throw new ArgumentNullException(nameof(model));
}
}
}

View File

@ -0,0 +1,11 @@
using LFlow.Base.Interfaces;
using LFlow.Role.Service;
using Serilog;
namespace LFlow.Role.Controller
{
public class RoleController(IRoleService service, ILogger logger) : BaseController
{
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<EnableDynamicLoading>true</EnableDynamicLoading>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>..\LFlow_Bin\Services\Role.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
<ProjectReference Include="..\LFlow.Permission\LFlow.Permission.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
using LFlow.Base.Interfaces;
using LFlow.Permission.Model;
namespace LFlow.Role.Model
{
public class RoleDto : IModel
{
public string? ID
{
get;
set;
}
public string? RoleName
{
get;
set;
}
public string? RoleDesc
{
get;
set;
}
public string? RoleType
{
get;
set;
}
public string? RoleStatus
{
get;
set;
}
public bool? IsDefault
{
get;
set;
}
public bool? IsEnable
{
get;
set;
}
/// <summary>
/// 权限列表
/// </summary>
public List<PermissionDto> Permissions { get; set; } = [];
}
}

View File

@ -0,0 +1,52 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.Role.Model
{
[SugarTable("T_U_ROLE")]
public class RoleModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID
{
get;
set;
}
public string RoleName
{
get;
set;
}
public string RoleDesc
{
get;
set;
}
public string RoleType
{
get;
set;
}
public string RoleStatus
{
get;
set;
}
public bool IsDefault
{
get;
set;
}
public bool IsEnable
{
get;
set;
}
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.Role.Model;
using SqlSugar;
namespace LFlow.Role.Repository
{
internal class RoleRepo(ISqlSugarClient client) : DefaultCurdRepo<RoleModel, string>(client)
{
}
}

24
LFlow.Role/RoleModule.cs Normal file
View File

@ -0,0 +1,24 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Role.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.Role
{
public class RoleModule : IModule
{
public void ConfigureModule(IServiceCollection services)
{
CodeFirst.AddType(typeof(RoleModel));
var assembly = Assembly.GetAssembly(typeof(RoleModule))!;
var types = assembly.GetTypes().ToList();
RegisterModule.RegisterAllService(types, services);
RegisterModule.RegisterAllRepo(types, services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManegementModule ConfigureModule done");
}
}
}

View File

@ -0,0 +1,21 @@
using LFlow.Base.Interfaces;
using LFlow.Role.Model;
namespace LFlow.Role.Service
{
public interface IRoleService : IService//<VersionDto>
{
Task<List<RoleDto>> GetRoleListAsync(int pageIndex, int pageSize, ref int total);
Task<RoleDto> GetRoleAsync(string id);
Task<RoleDto> AddRoleAsync(RoleDto model);
Task<RoleDto> UpdateRoleAsync(RoleDto model);
Task<int> DeleteRoleAsync(string id);
/// <summary>
/// 获取角色权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
Task<RoleDto> GetRolePerminssionListAsync(string roleId);
}
}

View File

@ -0,0 +1,103 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Permission.Service;
using LFlow.Role.Model;
using Mapster;
namespace LFlow.Role.Service
{
/// <summary>
/// 角色服务
/// </summary>
public class RoleService(IRepo<RoleModel, string> repo, IPermissionService permissionService, ISelfCache cacher) : IRoleService
{
/// <summary>
/// 添加角色
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<RoleDto> AddRoleAsync(RoleDto model)
{
var savedRole = repo.SaveOrUpdate(model.Adapt<RoleModel>(), isUpdate: false);
if (savedRole == null)
{
throw new InvalidOperationException("Failed to save or update the role.");
}
var roleDto = savedRole.MapTo<RoleDto>();
if (roleDto == null)
{
throw new InvalidOperationException("Failed to map the saved role to RoleDto.");
}
return Task.FromResult(roleDto);
}
/// <summary>
/// 删除角色并清理缓存
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Task<int> DeleteRoleAsync(string id)
{
cacher.RemoveAsync(id);
return Task.FromResult(repo.DeleteById(id));
}
public async Task<RoleDto> GetRolePerminssionListAsync(string roleId)
{
var roleDto = GetRoleAsync(roleId).Result;
if (roleDto == null)
{
return new RoleDto();
}
var permissions = await permissionService.GetProgPerminssionListAsync(roleId);
if (permissions != null && permissions.Count >= 0)
{
roleDto.Permissions = permissions!;
return roleDto;
}
else
{
return new RoleDto();
}
}
/// <summary>
/// 根据ID获取角色
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<RoleDto> GetRoleAsync(string id)
{
var cachedRole = await cacher.GetAsync<RoleDto>(id);
if (cachedRole != null)
{
return cachedRole;
}
var role = await Task.FromResult(repo.Get(id));
if (role != null)
{
await cacher.SetAsync(id, role);
}
return role?.MapTo<RoleDto>() ?? new RoleDto();
}
/// <summary>
/// 获取角色列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="total"></param>
/// <returns></returns>
public Task<List<RoleDto>> GetRoleListAsync(int pageIndex, int pageSize, ref int total)
{
var roleModels = repo.GetAll(pageIndex, pageSize, ref total);
var roleDtos = roleModels?.Select(role => role.MapTo<RoleDto>()).ToList() ?? [];
return Task.FromResult(roleDtos ?? [])!;
}
public Task<RoleDto> UpdateRoleAsync(RoleDto model)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,50 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.UserManagement.Model;
using LFlow.UserManagement.Service;
using Microsoft.AspNetCore.Mvc;
using Serilog;
namespace LFlow.UserManagement.Controller
{
/// <summary>
/// 用户管理
/// </summary>
public class UserManagementController(IUserManagementService service, ILogger logger) : BaseController
{
/// <summary>
/// 登录
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public ApiResult<UserDto>? Login(UserDto user)
{
var loginedUser = service.Login(user);
if (loginedUser == null)
{
//logger.Error("登录失败,用户名或密码错误");
return Fail(loginedUser, "登录失败,用户名或密码错误", 10001);
}
return Success(loginedUser);
}
/// <summary>
/// 注册
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public ApiResult<UserDto> Register(UserDto user)
{
try
{
return Success(service.Register(user));
}
catch (Exception ex)
{
return Fail<UserDto>(null, ex.Message, 500);
}
}
}
}

View File

@ -0,0 +1,17 @@
using LFlow.InternalEventBus.Interface;
using Serilog;
namespace LFlow.UserManagement
{
internal class InitEventSubscriber(ILogger logger) : IInternalEventSubscriber
{
public int Priority => 1;
public string EventName => "Init";
public Task<bool> Handle(object data)
{
logger.Information($"Init event received. data => {data}");
return Task.FromResult(true);
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>..\LFlow_Bin\Services\UserManagement.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
<ProjectReference Include="..\LFlow.Role\LFlow.Role.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@
using LFlow.Base.Interfaces;
using LFlow.Role.Model;
namespace LFlow.UserManagement.Model
{
public class UserDto : IModel
{
public string? ID
{
get;
set;
}
public string? UserName
{
get;
set;
}
public string? UserEmail
{
get; set;
}
public string? UserPassword
{
get; set;
}
public RoleDto? UserRole
{
get; set;
}
public string? Token
{
get; set;
}
}
}

View File

@ -0,0 +1,31 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.UserManagement.Model
{
[SugarTable("T_U_USERINFO")]
public class UserModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID
{
get;
set;
}
public string UserName
{
get;
set;
}
public string UserEmail
{
get; set;
}
public string UserPassword
{
get; set;
}
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.UserManagement.Model;
using SqlSugar;
namespace LFlow.UserManagement.Repository
{
internal class UserManagementRepo(ISqlSugarClient client) : DefaultCurdRepo<UserModel, string>(client)
{
}
}

View File

@ -0,0 +1,14 @@
using LFlow.Base.Interfaces;
using LFlow.UserManagement.Model;
namespace LFlow.UserManagement.Service
{
public interface IUserManagementService : IService//<VersionDto>
{
UserDto? Login(UserDto user);
Task<UserDto>? LoginAsync(UserDto user);
UserDto? Register(UserDto user);
Task<UserDto>? RegisterAsync(UserDto user);
}
}

View File

@ -0,0 +1,84 @@
using Azure.Core;
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.UserManagement.Model;
using LFlow.UserManagement.Util;
using Microsoft.AspNetCore.Http;
namespace LFlow.UserManagement.Service
{
/// <summary>
/// 在线管理服务
/// </summary>
public class UserManagementService(IRepo<UserModel, string> repo, ISelfCache cacher) : IUserManagementService
{
public UserDto? Login(UserDto user)
{
var userPwd = PasswordHelper.HashPassword(user.UserPassword);
var userModel = repo.WhereSearchId(new UserModel
{
UserName = user!.UserName!,
UserPassword = userPwd
});
if (userModel == null || !userModel.Any())
{
return null;
}
var firstUserId = userModel.FirstOrDefault();
if (firstUserId == null)
{
return null;
}
var loginedUser = repo.Get(firstUserId);
loginedUser.UserPassword = "";
var token = Guid.NewGuid().ToString();
// loginedUser.Token = token;
cacher.SetAsync(token, loginedUser, TimeSpan.FromHours(2));
var result = loginedUser.MapTo<UserDto>();
if(result != null){
result.Token = token;
}
return result;
}
public Task<UserDto>? LoginAsync(UserDto user)
{
return Task.FromResult(this.Login(user));
}
public UserDto? Register(UserDto user)
{
if (user.UserEmail == null || user.UserName == null || user.UserPassword == null)
{
throw new Exception("用户名、密码、邮箱不能为空");
}
var userPwd = PasswordHelper.HashPassword(user.UserPassword);
var ids = repo.WhereSearchId(new UserModel
{
UserName = user.UserName
});
if (ids != null && ids.Count > 0)
{
throw new Exception("用户名已存在");
}
var userModel = new UserModel
{
ID = Guid.NewGuid().ToString(),
UserName = user.UserName,
UserEmail = user.UserEmail,
UserPassword = userPwd
};
var savedUser = repo.SaveOrUpdate(userModel, false).MapTo<UserDto>();
savedUser.UserPassword = "";
return savedUser;
}
public Task<UserDto> RegisterAsync(UserDto user)
{
return Task.FromResult(this.Register(user));
}
}
}

View File

@ -0,0 +1,31 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.UserManagement.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.UserManagement
{
/// <summary>
/// 用户管理模块
/// </summary>
public class UserManagementModule : IModule
{
/// <summary>
/// 配置模块
/// </summary>
/// <param name="services"></param>
public void ConfigureModule(IServiceCollection services)
{
CodeFirst.AddType(typeof(UserModel));
var assembly = Assembly.GetAssembly(typeof(UserManagementModule))!;
var types = assembly.GetTypes().ToList();
types.RegisterAllService(services);
types.RegisterAllRepo(services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManagementModule ConfigureModule done");
}
}
}

View File

@ -0,0 +1,65 @@
using LFlow.Base;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Middleware;
using LFlow.Permission.Service;
using LFlow.UserManagement.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Newtonsoft.Json;
using Serilog;
namespace LFlow.UserManagement
{
/// <summary>
/// 用户管理中间件
/// </summary>
/// <param name="logger"></param>
public class UserMiddleware(ILogger logger,ISelfCache selfCache) : ILFlowMiddleware
{
/// <summary>
/// 优先级
/// </summary>
public int Priority => 1;
/// <summary>
/// 执行入口
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
public async Task RunAsync(Microsoft.AspNetCore.Http.HttpContext context, Func<Task> next)
{
var progName = context.GetRouteData()?.Values["controller"]?.ToString();
var progAction = context.GetRouteData()?.Values["action"]?.ToString();
if (progName != null)
{
var service = App.GetService<IPermissionService>();
var progPermission = service != null ? await service.GetProgPerminssionListAsync(progName) : null;
var currentPermission = progPermission?.FirstOrDefault(p => p.PermissionAction == progAction);
if (currentPermission == null || currentPermission!.IsPublic)
{
await next();
}
else
{
//TODO 从缓存中根据Token获取用户信息并判断是否有权限
await context.Response.WriteAsync(JsonConvert.SerializeObject(ApiResult<object>.FailResult("无权限!", 100501)));
var token = context.Request.Cookies["Token"]?.ToString();
if (token != null)
{
var user = selfCache.GetAsync<UserModel>(token!);
var userPermissions = service?.GetPermissions();
}
else
{
await context.Response.WriteAsync(JsonConvert.SerializeObject(ApiResult<object>.FailResult("未登录!", 100500)));
}
}
}
// 预检请求
if (context.Request.Method == "OPTIONS")
{
await next();
}
}
}
}

View File

@ -0,0 +1,28 @@
using System.Security.Cryptography;
using System.Text;
namespace LFlow.UserManagement.Util
{
/// <summary>
///
/// </summary>
public class PasswordHelper
{
/// <summary>
///
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
public static string HashPassword(string password)
{
byte[] data = Encoding.Default.GetBytes(password);
byte[] hashData = MD5.HashData(data);
StringBuilder sb = new();
foreach (byte b in hashData)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,99 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.VersionManagement.Enums;
using LFlow.VersionManagement.Model;
using LFlow.VersionManagement.Service;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Minio;
using SqlSugar;
namespace LFlow.VersionManagement.Controller;
public class VersionManagementController(IVersionManagementService service, IMinioClient _client) : BaseController
{
[HttpDelete]
public int DeleteById(string id)
{
return service.DeleteById(id);
}
[HttpGet]
public PagedApiResult<List<VersionDto>> GetAll(int pageIndex, int pageSize)
{
var total = 0;
var result = service.GetAll(pageIndex, pageSize, ref total);
return PagedApiResult<List<VersionDto>>.SuccessResult(
data: Mapper.Map<List<VersionDto>>(result) ?? [],
total,
pageIndex,
pageSize
);
//return new PagedApiResult<List<VersionDto>>
//{
// Data = Mapper.Map<List<VersionDto>>(result),
// PageIndex = pageIndex,
// PageSize = pageSize,
// Message = "获取成功",
// Code = 200,
// Success = true,
// TotalCount = total
//};
}
[HttpGet]
public ApiResult<VersionDto> GetById(string id)
{
var result = service.GetById(id).Adapt<VersionDto>();
return ApiResult<VersionDto>.SuccessResult(result);
}
[HttpPost]
public ApiResult<VersionDto> Save(VersionDto entity, bool isUpdate)
{
if (!isUpdate)
{
entity.ID = Guid.NewGuid().ToString();
entity.DownloadUrl = "/";
}
var result = service.Save(entity, isUpdate).Adapt<VersionDto>();
return ApiResult<VersionDto>.SuccessResult(result);
}
/// <summary>
/// 保存文件
/// </summary>
/// <param name="formFile"></param>
/// <returns></returns>
[HttpPost("upload")]
public async Task<IActionResult> SaveFile(IFormFile formFile)
{
if (formFile == null || formFile.Length == 0)
{
return BadRequest("No file uploaded.");
}
MinIOService minIOService = new MinIOService(_client);
var guid = await minIOService.Upload(formFile);
return Ok(guid);
}
[HttpPost]
public List<VersionDto> Search(VersionDto whereObj)
{
return service.Search(whereObj).Adapt<List<VersionDto>>();
}
//[HttpPost]
//public ApiResult<VersionDto> CheckUpdate(VersionDto current)
//{
//}
[HttpGet]
public ApiResult<VersionDto> GetLastUpdate(VersionChannel channel, UpgradeTargetType targetType)
{
return ApiResult<VersionDto>.SuccessResult(service.GetLastVersion(channel, targetType));
}
}

View File

@ -8,14 +8,12 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>..\LFlow_Bin\Services\VersionManagement.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controller\" />
</ItemGroup>
</Project>

View File

@ -37,6 +37,7 @@ public class VersionDto : IModel
{
get; set;
}
#region
/// <summary>
/// 文件名
/// </summary>
@ -47,7 +48,7 @@ public class VersionDto : IModel
/// <summary>
/// 文件大小
/// </summary>
public string? FileSize
public double? FileSize
{
get; set;
}
@ -59,6 +60,14 @@ public class VersionDto : IModel
get; set;
}
/// <summary>
/// 文件最后修改时间
/// </summary>
public DateTime LastModifyTime
{
get; set;
}
#endregion
/// <summary>
/// 是否强制更新
/// </summary>
public bool IsRequired

View File

@ -54,7 +54,7 @@ public class VersionModel : IDataModel
/// <summary>
/// 文件大小
/// </summary>
public string? FileSize
public double? FileSize
{
get; set;
}
@ -66,6 +66,13 @@ public class VersionModel : IDataModel
get; set;
}
/// <summary>
/// 文件最后修改时间
/// </summary>
public DateTime LastModifyTime
{
get; set;
}
/// <summary>
/// 是否强制更新
/// </summary>
public bool IsRequired

View File

@ -1,17 +1,28 @@
using LFlow.Base.Default;
using LFlow.Base.Utils;
using LFlow.VersionManagement.Enums;
using LFlow.VersionManagement.Model;
using SqlSugar;
namespace LFlow.VersionManagement.Repository;
public class VersionManagementRepo(ISqlSugarClient db) : DefaultCurdRepo<VersionModel, string>(db)
{
/// <summary>
/// 根据条件搜索
/// </summary>
/// <param name="whereObj"></param>
/// <returns></returns>
public override List<VersionModel> Search(VersionModel whereObj)
{
return db.Queryable<VersionModel>()
.Where(whereObj.ToWhereExp())
.ToList();
}
/// <summary>
/// 根据条件搜索ID
/// </summary>
/// <param name="whereObj"></param>
/// <returns></returns>
public override List<string> WhereSearchId(VersionModel whereObj)
{
return db.Queryable<VersionModel>()
@ -20,4 +31,19 @@ public class VersionManagementRepo(ISqlSugarClient db) : DefaultCurdRepo<Version
.Select(x => x.ID)
.ToList();
}
/// <summary>
/// 获取最新版本
/// </summary>
/// <param name="channel"></param>
/// <param name="targetType"></param>
/// <returns></returns>
public VersionModel GetLatestVersion(VersionChannel channel, UpgradeTargetType targetType)
{
return db.Queryable<VersionModel>()
.Where(mt => channel == mt.VersionChannel && targetType == mt.UpgradeTargetType)
.GroupBy(mt => mt.CurrentVersion)
.OrderBy(mt => mt.LastPublishTime, OrderByType.Desc)
.First();
}
}

View File

@ -1,7 +1,17 @@
using LFlow.Base.Interfaces;
using LFlow.VersionManagement.Enums;
using LFlow.VersionManagement.Model;
namespace LFlow.VersionManagement.Service;
public interface IVersionManagementService : IService<VersionDto>
public interface IVersionManagementService : IService//<VersionDto>
{
VersionDto GetById(string id);
List<VersionDto> Search(VersionDto whereObj);
int DeleteById(string id);
VersionDto Save(VersionDto entity, bool isUpdate);
List<VersionDto> GetAll(int pageIndex, int pageSize, ref int total);
VersionDto? GetLastVersion(VersionChannel channel, UpgradeTargetType targetType);
}

View File

@ -1,60 +1,37 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.VersionManagement.Enums;
using LFlow.VersionManagement.Model;
using LFlow.VersionManagement.Repository;
using Mapster;
using Microsoft.AspNetCore.Mvc;
namespace LFlow.VersionManagement.Service;
/// <summary>
/// 版本管理服务
/// </summary>
public class VersionManagementService : BaseController, IVersionManagementService
public class VersionManagementService : IVersionManagementService
{
private readonly IRepo<VersionModel, string> _repo;
public VersionManagementService(IRepo<VersionModel, string> repo)
{
_repo = repo;
}
[HttpDelete]
public int DeleteById(string id)
{
return _repo.DeleteById(id);
}
[HttpGet]
public PagedApiResult<List<VersionDto>> GetAll(int pageIndex, int pageSize)
{
var total = 0;
var result = _repo.GetAll(pageIndex, pageSize, ref total);
return new PagedApiResult<List<VersionDto>>
{
Data = Mapper.Map<List<VersionDto>>(result),
PageIndex = pageIndex,
PageSize = pageSize,
Message = "获取成功",
Success = true,
TotalCount = total
};
}
[HttpGet]
public VersionDto GetById(string id)
{
return _repo.Get(id).Adapt<VersionDto>();
}
[HttpPost]
public VersionDto Save(VersionDto entity, bool isUpdate)
{
return _repo.SaveOrUpdate(entity.Adapt<VersionModel>(), isUpdate).Adapt<VersionDto>();
}
[HttpPost]
public int DeleteById(string id) => _repo.DeleteById(id);
public List<VersionDto> GetAll(int pageIndex, int pageSize, ref int total) => _repo.GetAll(pageIndex, pageSize, ref total).Adapt<List<VersionDto>>();
public VersionDto GetById(string id) => _repo.Get(id).Adapt<VersionDto>();
public VersionDto Save(VersionDto entity, bool isUpdate) => _repo.SaveOrUpdate(entity.Adapt<VersionModel>(), isUpdate).Adapt<VersionDto>();
// 搜索需要增加分页
public List<VersionDto> Search(VersionDto whereObj)
=> _repo.Search(whereObj.Adapt<VersionModel>()).Adapt<List<VersionDto>>();
public VersionDto? GetLastVersion(VersionChannel channel, UpgradeTargetType targetType)
{
return _repo.Search(whereObj.Adapt<VersionModel>()).Adapt<List<VersionDto>>();
}
[HttpPost]
public List<string> SearchAllId(VersionDto whereObj)
{
return _repo.WhereSearchId(whereObj.Adapt<VersionModel>());
if (_repo is VersionManagementRepo versionRepo)
{
return versionRepo.GetLatestVersion(channel, targetType).Adapt<VersionDto>();
}
else
{
return default;
}
}
}

View File

@ -1,10 +1,11 @@
using System.Reflection;
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.VersionManagement.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.User;
namespace LFlow.VersionManagement;
public class VersionManagementModule : IModule
{
@ -13,10 +14,11 @@ public class VersionManagementModule : IModule
CodeFirst.AddType(typeof(VersionModel));
var assembly = Assembly.GetAssembly(typeof(VersionManagementModule))!;
var types = assembly.GetTypes().ToList();
RegisterModule.RegisterAllService(types, services);
RegisterModule.RegisterAllRepo(types, services);
RegisterModule.RegisterAllModel(types, services);
types.RegisterAllService(services);
types.RegisterAllRepo(services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Console.WriteLine("UserModule ConfigureModule");
Log.Logger?.Information("VersionManagementModule ConfigureModule done");
//Console.WriteLine("UserModule ConfigureModule");
}
}

View File

@ -5,7 +5,21 @@ VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.Base", "LFlow.Base\LFlow.Base.csproj", "{C581AFB2-50BA-4357-AD0A-AF287194837D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.VersionManagement", "LFlow.VersionManagement\LFlow.VersionManagement.csproj", "{D52CC594-B10C-4FC0-8B7A-68CE0645A95D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.VersionManagement", "LFlow.VersionManagement\LFlow.VersionManagement.csproj", "{D52CC594-B10C-4FC0-8B7A-68CE0645A95D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.OnlineManegement", "LFlow.OnlineManegement\LFlow.OnlineManegement.csproj", "{E849310F-64AD-453A-A2AA-557731508BF1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.UserManagement", "LFlow.UserManagement\LFlow.UserManagement.csproj", "{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.Middleware", "LFlow.Middleware\LFlow.Middleware.csproj", "{5BFD207E-28B3-40B8-94DF-1723C6A4424B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.InternalEventBus", "LFlow.InternalEventBus\LFlow.InternalEventBus.csproj", "{72CB0C72-9725-43A5-882D-3E93FD1F8706}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.Cache", "LFlow.Cache\LFlow.Cache.csproj", "{97D56496-BE2A-4431-A882-7DEA20B72EB0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Role", "LFlow.Role\LFlow.Role.csproj", "{05CEB068-167F-4B27-A59A-EFBF0A656C43}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Permission", "LFlow.Permission\LFlow.Permission.csproj", "{E7C112C2-534C-4212-959F-FBB4E21A41C1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -21,6 +35,34 @@ Global
{D52CC594-B10C-4FC0-8B7A-68CE0645A95D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D52CC594-B10C-4FC0-8B7A-68CE0645A95D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D52CC594-B10C-4FC0-8B7A-68CE0645A95D}.Release|Any CPU.Build.0 = Release|Any CPU
{E849310F-64AD-453A-A2AA-557731508BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E849310F-64AD-453A-A2AA-557731508BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E849310F-64AD-453A-A2AA-557731508BF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E849310F-64AD-453A-A2AA-557731508BF1}.Release|Any CPU.Build.0 = Release|Any CPU
{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}.Release|Any CPU.Build.0 = Release|Any CPU
{5BFD207E-28B3-40B8-94DF-1723C6A4424B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BFD207E-28B3-40B8-94DF-1723C6A4424B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BFD207E-28B3-40B8-94DF-1723C6A4424B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BFD207E-28B3-40B8-94DF-1723C6A4424B}.Release|Any CPU.Build.0 = Release|Any CPU
{72CB0C72-9725-43A5-882D-3E93FD1F8706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72CB0C72-9725-43A5-882D-3E93FD1F8706}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72CB0C72-9725-43A5-882D-3E93FD1F8706}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72CB0C72-9725-43A5-882D-3E93FD1F8706}.Release|Any CPU.Build.0 = Release|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Release|Any CPU.Build.0 = Release|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Release|Any CPU.Build.0 = Release|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE