mirror of
https://gitee.com/dotnetchina/OpenAuth.Net.git
synced 2026-07-28 06:07:59 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8beafeb79a | ||
|
|
47245dbd8d | ||
|
|
4bf28c77a1 | ||
|
|
35300b9c43 | ||
|
|
f7230d9092 | ||
|
|
983e185fa9 | ||
|
|
d986c242b7 | ||
|
|
6f276a0587 | ||
|
|
edb40b1775 | ||
|
|
23de2ae7e2 | ||
|
|
8fd72ae0d3 | ||
|
|
57546e5175 | ||
|
|
d7b3ebb1ce | ||
|
|
56602c67f3 | ||
|
|
6f0c09d8d5 | ||
|
|
e3f6374ac1 | ||
|
|
628aa65938 | ||
|
|
04142acaaa | ||
|
|
c084e2409f | ||
|
|
4ed8c0819b | ||
|
|
217b407cd9 | ||
|
|
2fb964f9d0 | ||
|
|
6a8eb675e9 | ||
|
|
d7f48e0537 | ||
|
|
e6410c9da6 | ||
|
|
4730ac7a36 | ||
|
|
33cc0b1e3b | ||
|
|
87866d08fb |
@@ -9,13 +9,11 @@
|
||||
],
|
||||
"env": {}
|
||||
},
|
||||
"mysql": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"openauthpro": {
|
||||
"command": "powershell",
|
||||
"args": [
|
||||
"--from",
|
||||
"mysql-mcp-server",
|
||||
"mysql_mcp_server"
|
||||
"-Command",
|
||||
"npx -y @f4ww4z/mcp-mysql-server"
|
||||
],
|
||||
"env": {
|
||||
"MYSQL_HOST": "localhost",
|
||||
|
||||
238
.cursor/rules/openauth.mdc
Normal file
238
.cursor/rules/openauth.mdc
Normal file
@@ -0,0 +1,238 @@
|
||||
---
|
||||
alwaysApply: false
|
||||
---
|
||||
# OpenAuth.Net Cursor Rules
|
||||
|
||||
## 项目概述
|
||||
OpenAuth.Net是一个基于.NET 9的企业级权限管理和快速开发框架,采用Martin Fowler企业级应用开发思想,集成了最新的技术栈。
|
||||
|
||||
## 技术栈
|
||||
- **后端**: .NET 9, ASP.NET Core WebAPI
|
||||
- **ORM**: SqlSugar (主要) + Entity Framework Core (兼容)
|
||||
- **依赖注入**: Autofac
|
||||
- **数据库**: 支持SqlServer、MySQL、Oracle、PostgreSQL
|
||||
- **定时任务**: Quartz.NET
|
||||
- **缓存**: Redis, MemoryCache
|
||||
- **前端**: Vue2 + Element-UI
|
||||
- **测试**: NUnit
|
||||
- **文档**: Swagger
|
||||
|
||||
## 项目架构
|
||||
```
|
||||
📦OpenAuth.Net
|
||||
┣ 📂Infrastructure # 基础设施层 - 工具类、扩展方法、帮助类
|
||||
┣ 📂OpenAuth.Repository # 数据访问层 - 实体定义、数据访问
|
||||
┣ 📂OpenAuth.App # 应用服务层 - 业务逻辑
|
||||
┣ 📂OpenAuth.WebApi # 表示层 - WebAPI控制器
|
||||
┣ 📂OpenAuth.Identity # 身份认证服务 - IdentityServer4
|
||||
┣ 📂Vue2 # 前端项目
|
||||
┗ 📂数据库脚本 # 数据库初始化脚本
|
||||
```
|
||||
|
||||
## 编码规范
|
||||
|
||||
### 通用规范
|
||||
- 使用C# 9+语法特性
|
||||
- 遵循Microsoft C#编码规范
|
||||
- 类名使用PascalCase,方法名使用PascalCase
|
||||
- 私有字段使用_camelCase,公共属性使用PascalCase
|
||||
- 常量使用UPPER_CASE
|
||||
- 异步方法必须添加Async后缀
|
||||
- 修改完代码后不需要编译
|
||||
|
||||
### 命名规范
|
||||
- **Controller**: 以Controller结尾,如`UsersController`
|
||||
- **Service/App**: 以App结尾,如`UserManagerApp`
|
||||
- **Repository**: 以Repository结尾,如`UserRepository`
|
||||
- **Entity**: 实体类直接使用名词,如`SysUser`
|
||||
- **Request**: 请求类以Req结尾,如`QueryUserListReq`
|
||||
- **Response**: 响应类以Resp结尾,如`PagedListDataResp`
|
||||
- **DTO**: 视图模型以View结尾,如`UserView`
|
||||
|
||||
### 注释规范
|
||||
- 所有公共方法必须添加XML注释
|
||||
- 复杂业务逻辑必须添加行内注释
|
||||
- 使用/// <summary>标记方法说明
|
||||
- 参数使用/// <param name="参数名">说明</param>
|
||||
- 返回值使用/// <returns>说明</returns>
|
||||
|
||||
## 分层架构规则
|
||||
|
||||
### Infrastructure层 (基础设施层)
|
||||
- **职责**: 提供通用工具类、扩展方法、帮助类、常量定义
|
||||
- **规则**:
|
||||
- 不依赖其他业务层
|
||||
- 提供可复用的基础功能
|
||||
- 包含缓存、配置、工具类等
|
||||
- **文件组织**:
|
||||
- `/Cache` - 缓存相关
|
||||
- `/Extensions` - 扩展方法
|
||||
- `/Helpers` - 帮助类
|
||||
- `/Const` - 常量定义
|
||||
|
||||
### Repository层 (数据访问层)
|
||||
- **职责**: 数据访问、实体定义、数据库操作
|
||||
- **规则**:
|
||||
- 继承自`BaseRepository<T,TContext>`
|
||||
- 实体类继承自`StringEntity`、`LongEntity`或`IntAutoGenEntity`
|
||||
- 使用SqlSugar或EF Core进行数据访问
|
||||
- 包含DbContext配置
|
||||
- **基类使用**:
|
||||
- `StringEntity`: 字符串主键实体
|
||||
- `LongEntity`: 长整型主键实体
|
||||
- `IntAutoGenEntity`: 自增整型主键实体
|
||||
|
||||
### App层 (应用服务层)
|
||||
- **职责**: 业务逻辑处理、数据传输对象、业务规则
|
||||
- **规则**:
|
||||
- 继承自`SqlSugarBaseApp<T>`、`BaseStringApp<T>`、`BaseLongApp<T>`等基类
|
||||
- 通过构造函数注入依赖
|
||||
- 实现业务逻辑,不直接操作数据库
|
||||
- 使用Request/Response模式
|
||||
- **依赖注入**:
|
||||
```csharp
|
||||
public UserManagerApp(ISqlSugarClient client, RevelanceManagerApp app, IAuth auth)
|
||||
: base(client, auth)
|
||||
```
|
||||
|
||||
### WebApi层 (表示层)
|
||||
- **职责**: HTTP请求处理、参数验证、响应格式化
|
||||
- **规则**:
|
||||
- 继承自`ControllerBase`
|
||||
- 使用`[ApiController]`特性
|
||||
- 统一返回`Response<T>`格式
|
||||
- 进行参数验证
|
||||
- 添加Swagger文档注释
|
||||
|
||||
## 数据库操作规范
|
||||
|
||||
### SqlSugar使用规范
|
||||
```csharp
|
||||
// 查询
|
||||
var users = SugarClient.Queryable<SysUser>()
|
||||
.Where(u => u.Status == 1)
|
||||
.ToList();
|
||||
|
||||
// 分页查询
|
||||
var result = new PagedListDataResp<Role>();
|
||||
var objs = SugarClient.Queryable<Role>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
}
|
||||
|
||||
result.Data = await objs.OrderBy(u => u.Name)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).ToListAsync();
|
||||
result.Count = await objs.CountAsync();
|
||||
return result;
|
||||
|
||||
// 联表查询
|
||||
var result = SugarClient.Queryable<Relevance>()
|
||||
.LeftJoin<SysOrg>((u, o) => u.SecondId == o.Id)
|
||||
.Where((u, o) => u.FirstId == userId && u.RelKey == Define.USERORG)
|
||||
.Select((u, o) => o);
|
||||
return result.ToList();
|
||||
```
|
||||
|
||||
### 事务处理
|
||||
```csharp
|
||||
SugarClient.Ado.BeginTran();
|
||||
try
|
||||
{
|
||||
// 数据库操作
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
catch
|
||||
{
|
||||
SugarClient.Ado.RollbackTran();
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
## 权限和安全规范
|
||||
|
||||
### 权限验证
|
||||
- 使用`IAuth`接口获取当前用户信息
|
||||
- 通过`_auth.GetCurrentUser()`获取登录用户
|
||||
- 数据权限通过机构级联控制
|
||||
|
||||
## API设计规范
|
||||
|
||||
### 控制器设计
|
||||
```csharp
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly UserManagerApp _app;
|
||||
|
||||
public UsersController(UserManagerApp app)
|
||||
{
|
||||
_app = app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户列表
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<Response<PagedListDataResp<UserView>>> Get([FromQuery] QueryUserListReq request)
|
||||
{
|
||||
var result = await _app.Load(request);
|
||||
return Response.Ok(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 依赖注入规范
|
||||
|
||||
### Autofac配置
|
||||
- 在`AutofacExt.cs`中配置依赖注入
|
||||
- 实现`IDependency`接口的类自动注册
|
||||
- 使用构造函数注入
|
||||
|
||||
## 测试规范
|
||||
|
||||
### 单元测试
|
||||
- 使用NUnit框架
|
||||
- 继承自`TestBase`基类
|
||||
- 测试类以Test结尾
|
||||
- 测试方法使用描述性名称
|
||||
|
||||
### 集成测试
|
||||
- 使用`AutofacWebApplicationFactory`
|
||||
- 模拟HTTP请求
|
||||
- 验证完整的业务流程
|
||||
|
||||
## 工作流规范
|
||||
|
||||
### 流程定义
|
||||
- 使用`FlowScheme`定义流程模板
|
||||
- 通过`FlowInstance`管理流程实例
|
||||
- `FlowNode`表示流程节点
|
||||
|
||||
### 流程引擎
|
||||
- 使用`FlowRuntime`执行流程
|
||||
- 支持顺序、并行、条件等流程类型
|
||||
- 流程状态通过`FlowInstanceStatus`管理
|
||||
|
||||
## 定时任务规范
|
||||
|
||||
### Quartz使用
|
||||
- 继承自`IJob`接口
|
||||
- 在`QuartzService`中注册任务
|
||||
- 支持Cron表达式配置
|
||||
|
||||
## 文档规范
|
||||
|
||||
### 代码文档
|
||||
- 重要业务逻辑添加注释
|
||||
- 复杂算法添加说明
|
||||
- 配置文件添加说明注释
|
||||
|
||||
## 配置管理
|
||||
|
||||
### 配置文件
|
||||
- `appsettings.json`: 基础配置
|
||||
- `appsettings.Development.json`: 开发环境配置
|
||||
- `appsettings.Production.json`: 生产环境配置
|
||||
@@ -28,12 +28,17 @@ RUN dotnet publish -c Release -o /app/publish/identity
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
|
||||
# 设置 Production 环境变量
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV DOTNET_ENVIRONMENT=Production
|
||||
|
||||
# 复制 WebApi 发布文件
|
||||
COPY --from=build /app/publish/webapi ./webapi
|
||||
|
||||
# 复制 Identity 发布文件
|
||||
COPY --from=build /app/publish/identity ./identity
|
||||
|
||||
# 启动 WebApi, Mvc, 和 Identity,就算失败也保持运行,方便查询日志
|
||||
# 启动 WebApi 和 Identity,就算失败也保持运行,方便查询日志
|
||||
ENTRYPOINT ["sh", "-c", "cd webapi && dotnet OpenAuth.WebApi.dll & cd identity && dotnet OpenAuth.IdentityServer.dll || tail -f /dev/null"]
|
||||
|
||||
|
||||
117
Infrastructure/Helpers/TimeHelper.cs
Normal file
117
Infrastructure/Helpers/TimeHelper.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
|
||||
namespace Infrastructure.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间处理帮助类
|
||||
/// 统一处理时区问题,确保所有时间都使用正确的时区
|
||||
/// </summary>
|
||||
public static class TimeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前中国标准时间(UTC+8)
|
||||
/// </summary>
|
||||
/// <returns>当前中国标准时间</returns>
|
||||
public static DateTime Now => GetChinaStandardTime();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前中国标准时间(UTC+8)
|
||||
/// </summary>
|
||||
/// <returns>当前中国标准时间</returns>
|
||||
public static DateTime GetChinaStandardTime()
|
||||
{
|
||||
// 获取UTC时间
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
// 转换为中国标准时间(UTC+8)
|
||||
var chinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
|
||||
|
||||
// 如果找不到中国时区,使用手动计算(UTC+8)
|
||||
if (chinaTimeZone == null)
|
||||
{
|
||||
return utcNow.AddHours(8);
|
||||
}
|
||||
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(utcNow, chinaTimeZone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将UTC时间转换为中国标准时间
|
||||
/// </summary>
|
||||
/// <param name="utcTime">UTC时间</param>
|
||||
/// <returns>中国标准时间</returns>
|
||||
public static DateTime ConvertUtcToChinaTime(DateTime utcTime)
|
||||
{
|
||||
if (utcTime.Kind != DateTimeKind.Utc)
|
||||
{
|
||||
utcTime = DateTime.SpecifyKind(utcTime, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
var chinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
|
||||
|
||||
if (chinaTimeZone == null)
|
||||
{
|
||||
return utcTime.AddHours(8);
|
||||
}
|
||||
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, chinaTimeZone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将中国标准时间转换为UTC时间
|
||||
/// </summary>
|
||||
/// <param name="chinaTime">中国标准时间</param>
|
||||
/// <returns>UTC时间</returns>
|
||||
public static DateTime ConvertChinaTimeToUtc(DateTime chinaTime)
|
||||
{
|
||||
var chinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
|
||||
|
||||
if (chinaTimeZone == null)
|
||||
{
|
||||
return chinaTime.AddHours(-8);
|
||||
}
|
||||
|
||||
return TimeZoneInfo.ConvertTimeToUtc(chinaTime, chinaTimeZone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前时间的Unix时间戳(秒)
|
||||
/// </summary>
|
||||
/// <returns>Unix时间戳</returns>
|
||||
public static long GetUnixTimestamp()
|
||||
{
|
||||
return ((DateTimeOffset)Now).ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前时间的Unix时间戳(毫秒)
|
||||
/// </summary>
|
||||
/// <returns>Unix时间戳(毫秒)</returns>
|
||||
public static long GetUnixTimestampMilliseconds()
|
||||
{
|
||||
return ((DateTimeOffset)Now).ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将Unix时间戳转换为中国标准时间
|
||||
/// </summary>
|
||||
/// <param name="timestamp">Unix时间戳(秒)</param>
|
||||
/// <returns>中国标准时间</returns>
|
||||
public static DateTime FromUnixTimestamp(long timestamp)
|
||||
{
|
||||
var utcTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
||||
return ConvertUtcToChinaTime(utcTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将Unix时间戳(毫秒)转换为中国标准时间
|
||||
/// </summary>
|
||||
/// <param name="timestamp">Unix时间戳(毫秒)</param>
|
||||
/// <returns>中国标准时间</returns>
|
||||
public static DateTime FromUnixTimestampMilliseconds(long timestamp)
|
||||
{
|
||||
var utcTime = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime;
|
||||
return ConvertUtcToChinaTime(utcTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\Infrastructure.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\Infrastructure.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
58
OpenAuth.App/Base/SqlSugarBaseTreeApp.cs
Normal file
58
OpenAuth.App/Base/SqlSugarBaseTreeApp.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.Repository.Core;
|
||||
using SqlSugar;
|
||||
using OpenAuth.Repository.Interface;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
/// <summary>
|
||||
/// 树状结构处理
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// /// <typeparam name="TDbContext"></typeparam>
|
||||
public class SqlSugarBaseTreeApp<T> :SqlSugarBaseApp<T> where T : TreeEntity, new()
|
||||
{
|
||||
|
||||
|
||||
public SqlSugarBaseTreeApp(ISqlSugarClient client, IAuth auth)
|
||||
: base(client, auth)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新树状结构实体
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
public void UpdateTreeObj<U>(U obj) where U : TreeEntity, new()
|
||||
{
|
||||
CaculateCascade(obj);
|
||||
|
||||
//获取旧的的CascadeId
|
||||
var cascadeId = SugarClient.Queryable<U>().First(o => o.Id == obj.Id).CascadeId;
|
||||
//根据CascadeId查询子部门
|
||||
var objs = SugarClient.Queryable<U>().Where(u => u.CascadeId.Contains(cascadeId) && u.Id != obj.Id)
|
||||
.OrderBy(u => u.CascadeId).ToList();
|
||||
|
||||
//更新操作
|
||||
SugarClient.Updateable(obj).ExecuteCommand();
|
||||
|
||||
//更新子模块的CascadeId
|
||||
foreach (var a in objs)
|
||||
{
|
||||
a.CascadeId = a.CascadeId.Replace(cascadeId, obj.CascadeId);
|
||||
if (a.ParentId == obj.Id)
|
||||
{
|
||||
a.ParentName = obj.Name;
|
||||
}
|
||||
|
||||
SugarClient.Updateable(a).ExecuteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -212,21 +212,35 @@ namespace OpenAuth.App
|
||||
, case when con.conname is not null then 1 else 0 end as iskey --是否主键
|
||||
, case when attr.attnotnull is true then 0 else 1 end as isnull --可空
|
||||
, t.typname columntype --类型名称
|
||||
, case
|
||||
when typname in ('BIT', 'BOOL', 'bit', 'bool') then
|
||||
'bool'
|
||||
when typname in ('smallint', 'SMALLINT') then 'short'
|
||||
when typname in ('tinyint', 'TINYINT') then 'bool'
|
||||
when typname in ('int4', 'int', 'INT') then
|
||||
'int'
|
||||
when typname in ('BIGINT', 'bigint') then
|
||||
'bigint'
|
||||
when typname in ('FLOAT', 'DOUBLE', 'DECIMAL', 'float', 'double', 'decimal') then
|
||||
'decimal'
|
||||
when typname in ('varchar', 'text') then
|
||||
'string'
|
||||
when typname in ('Date', 'DateTime', 'TimeStamp', 'date', 'datetime', 'timestamp') then
|
||||
'DateTime'
|
||||
,case
|
||||
-- 布尔类型
|
||||
when typname in ('bit', 'bool', 'boolean') then 'bool'
|
||||
|
||||
-- 整数类型
|
||||
when typname in ('int2', 'smallint') then 'short'
|
||||
when typname in ('int4', 'int', 'integer') then 'int'
|
||||
when typname in ('int8', 'bigint') then 'bigint'
|
||||
|
||||
-- 浮点数类型
|
||||
when typname in ('real', 'float4') then 'float'
|
||||
when typname in ('float8', 'double precision') then 'double'
|
||||
when typname in ('numeric', 'decimal') then 'decimal'
|
||||
|
||||
-- 字符串类型
|
||||
when typname in ('char', 'bpchar', 'character') then 'string'
|
||||
when typname in ('varchar', 'character varying') then 'string'
|
||||
when typname in ('text') then 'string'
|
||||
|
||||
-- 日期时间类型
|
||||
when typname in ('time', 'timetz') then 'TimeSpan'
|
||||
when typname in ('timestamp', 'timestamptz', 'datetime','date') then 'DateTime'
|
||||
when typname in ('interval') then 'TimeSpan'
|
||||
|
||||
-- 其他类型
|
||||
when typname in ('uuid') then 'Guid'
|
||||
when typname in ('bytea') then 'byte[]'
|
||||
when typname in ('json', 'jsonb') then 'string'
|
||||
|
||||
else 'string'
|
||||
end as entitytype
|
||||
, comment.description as comment
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace OpenAuth.App
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void SaveFile(string fileName, byte[] fileBuffers)
|
||||
{
|
||||
string folder = DateTime.Now.ToString("yyyyMMdd");
|
||||
string folder = TimeHelper.Now.ToString("yyyyMMdd");
|
||||
|
||||
//判断文件是否为空
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
|
||||
@@ -15,6 +15,7 @@ using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using SqlSugar;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Infrastructure.Helpers;
|
||||
|
||||
namespace OpenAuth.App.Flow
|
||||
{
|
||||
@@ -221,7 +222,7 @@ namespace OpenAuth.App.Flow
|
||||
}
|
||||
|
||||
var content =
|
||||
$"{user.Account}-{DateTime.Now:yyyy-MM-dd HH:mm}审批了【{Nodes[canCheckId].name}】" +
|
||||
$"{user.Account}-{TimeHelper.Now:yyyy-MM-dd HH:mm}审批了【{Nodes[canCheckId].name}】" +
|
||||
$"结果:{(tag.Taged == 1 ? "同意" : "不同意")},备注:{tag.Description}";
|
||||
SaveOperationHis(content);
|
||||
|
||||
@@ -389,7 +390,7 @@ namespace OpenAuth.App.Flow
|
||||
sugarClient.Updateable(flowInstance).ExecuteCommand();
|
||||
|
||||
SaveOperationHis(
|
||||
$"{user.Account}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}驳回了【{currentNode.name}】");
|
||||
$"{user.Account}-{TimeHelper.Now.ToString("yyyy-MM-dd HH:mm")}驳回了【{currentNode.name}】");
|
||||
|
||||
NotifyThirdParty(client, currentNode, tag);
|
||||
}
|
||||
@@ -437,7 +438,7 @@ namespace OpenAuth.App.Flow
|
||||
item.Value.setInfo.UserId = tag.UserId;
|
||||
item.Value.setInfo.UserName = tag.UserName;
|
||||
item.Value.setInfo.Description = tag.Description;
|
||||
item.Value.setInfo.TagedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
|
||||
item.Value.setInfo.TagedTime = TimeHelper.Now.ToString("yyyy-MM-dd HH:mm");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -462,7 +463,7 @@ namespace OpenAuth.App.Flow
|
||||
InstanceId = flowInstanceId,
|
||||
CreateUserId = userId,
|
||||
CreateUserName = userName,
|
||||
CreateDate = DateTime.Now,
|
||||
CreateDate = TimeHelper.Now,
|
||||
Content = opHis
|
||||
}; //操作记录
|
||||
|
||||
@@ -744,7 +745,7 @@ namespace OpenAuth.App.Flow
|
||||
ApproveType = node.setInfo.NodeConfluenceType,
|
||||
ReturnToSignNode = false,
|
||||
Reason = "",
|
||||
CreateDate = DateTime.Now
|
||||
CreateDate = TimeHelper.Now
|
||||
};
|
||||
sugarClient.Insertable(flowApprover).ExecuteCommand();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
|
||||
* @Date: 2024-12-13 16:55:17
|
||||
* @Description: 工作流实例表操作
|
||||
* @LastEditTime: 2025-04-21 10:54:45
|
||||
* @LastEditTime: 2025-10-18 14:35:01
|
||||
* Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
*/
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace OpenAuth.App
|
||||
val = "";
|
||||
break;
|
||||
case "DateTime":
|
||||
val = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
val = TimeHelper.Now.ToString("yyyy-MM-dd");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ namespace OpenAuth.App
|
||||
}
|
||||
|
||||
var content =
|
||||
$"{user.Account}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}审批了【{wfruntime.currentNode.name}】" +
|
||||
$"{user.Account}-{TimeHelper.Now.ToString("yyyy-MM-dd HH:mm")}审批了【{wfruntime.currentNode.name}】" +
|
||||
$"结果:{(tag.Taged == 1 ? "同意" : "不同意")},备注:{tag.Description}";
|
||||
wfruntime.SaveOperationHis(tag.UserId, tag.UserName, content);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Helpers;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
@@ -41,7 +42,7 @@ namespace OpenAuth.App
|
||||
{
|
||||
SchemeContent = flowScheme.SchemeContent,
|
||||
SchemeName = flowScheme.SchemeName,
|
||||
ModifyDate = DateTime.Now,
|
||||
ModifyDate = TimeHelper.Now,
|
||||
FrmId = flowScheme.FrmId,
|
||||
FrmType = flowScheme.FrmType,
|
||||
FrmUrlTemplate = flowScheme.FrmUrlTemplate,
|
||||
|
||||
@@ -11,6 +11,7 @@ using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using Infrastructure.Helpers;
|
||||
using Quartz;
|
||||
using SqlSugar;
|
||||
|
||||
@@ -62,7 +63,7 @@ namespace OpenAuth.App
|
||||
public void Add(AddOrUpdateOpenJobReq req)
|
||||
{
|
||||
var obj = req.MapTo<OpenJob>();
|
||||
obj.CreateTime = DateTime.Now;
|
||||
obj.CreateTime = TimeHelper.Now;
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.CreateUserId = user.Id;
|
||||
obj.CreateUserName = user.Name;
|
||||
@@ -81,7 +82,7 @@ namespace OpenAuth.App
|
||||
Cron = obj.Cron,
|
||||
Status = obj.Status,
|
||||
Remark = obj.Remark,
|
||||
UpdateTime = DateTime.Now,
|
||||
UpdateTime = TimeHelper.Now,
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name
|
||||
},u => u.Id == obj.Id);
|
||||
@@ -126,7 +127,7 @@ namespace OpenAuth.App
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
|
||||
job.Status = req.Status;
|
||||
job.UpdateTime = DateTime.Now;
|
||||
job.UpdateTime = TimeHelper.Now;
|
||||
job.UpdateUserId = user.Id;
|
||||
job.UpdateUserName = user.Name;
|
||||
Repository.Update(job);
|
||||
@@ -150,7 +151,7 @@ namespace OpenAuth.App
|
||||
}
|
||||
|
||||
job.RunCount++;
|
||||
job.LastRunTime = DateTime.Now;
|
||||
job.LastRunTime = TimeHelper.Now;
|
||||
Repository.Update(job);
|
||||
|
||||
_sysLogApp.Add(new SysLog
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\OpenAuth.App.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\OpenAuth.App.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DocumentationFile>bin\Release\net9.0\OpenAuth.App.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Model\**" />
|
||||
<EmbeddedResource Remove="Model\**" />
|
||||
|
||||
@@ -4,13 +4,15 @@ using System.Linq;
|
||||
using Infrastructure;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class OrgManagerApp : BaseTreeApp<SysOrg,OpenAuthDBContext>
|
||||
public class OrgManagerApp : SqlSugarBaseTreeApp<SysOrg>
|
||||
{
|
||||
private RevelanceManagerApp _revelanceApp;
|
||||
/// <summary>
|
||||
@@ -28,22 +30,20 @@ namespace OpenAuth.App
|
||||
}
|
||||
CaculateCascade(sysOrg);
|
||||
|
||||
UnitWork.ExecuteWithTransaction(() =>
|
||||
{
|
||||
UnitWork.Add(sysOrg);
|
||||
UnitWork.Save();
|
||||
SugarClient.Ado.BeginTran();
|
||||
Repository.Insert(sysOrg);
|
||||
|
||||
//如果当前账号不是SYSTEM,则直接分配
|
||||
if (loginContext.User.Account != Define.SYSTEM_USERNAME)
|
||||
//如果当前账号不是SYSTEM,则直接分配
|
||||
if (loginContext.User.Account != Define.SYSTEM_USERNAME)
|
||||
{
|
||||
_revelanceApp.Assign(new AssignReq
|
||||
{
|
||||
_revelanceApp.Assign(new AssignReq
|
||||
{
|
||||
type = Define.USERORG,
|
||||
firstId = loginContext.User.Id,
|
||||
secIds = new[] { sysOrg.Id }
|
||||
});
|
||||
}
|
||||
});
|
||||
type = Define.USERORG,
|
||||
firstId = loginContext.User.Id,
|
||||
secIds = new[] { sysOrg.Id }
|
||||
});
|
||||
}
|
||||
SugarClient.Ado.CommitTran();
|
||||
|
||||
return sysOrg.Id;
|
||||
}
|
||||
@@ -58,47 +58,48 @@ namespace OpenAuth.App
|
||||
|
||||
return sysOrg.Id;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定ID的部门及其所有子部门
|
||||
/// </summary>
|
||||
public void DelOrgCascade(string[] ids)
|
||||
{
|
||||
var delOrgCascadeIds = UnitWork.Find<SysOrg>(u => ids.Contains(u.Id)).Select(u => u.CascadeId).ToArray();
|
||||
var delOrgCascadeIds = SugarClient.Queryable<SysOrg>().Where(u => ids.Contains(u.Id)).Select(u => u.CascadeId).ToArray();
|
||||
var delOrgIds = new List<string>();
|
||||
foreach (var cascadeId in delOrgCascadeIds)
|
||||
{
|
||||
delOrgIds.AddRange(UnitWork.Find<SysOrg>(u=>u.CascadeId.Contains(cascadeId)).Select(u =>u.Id).ToArray());
|
||||
delOrgIds.AddRange(SugarClient.Queryable<SysOrg>().Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray());
|
||||
}
|
||||
|
||||
UnitWork.Delete<Relevance>(u =>u.RelKey == Define.USERORG && delOrgIds.Contains(u.SecondId));
|
||||
UnitWork.Delete<SysOrg>(u => delOrgIds.Contains(u.Id));
|
||||
UnitWork.Save();
|
||||
|
||||
|
||||
SugarClient.Deleteable<Relevance>().Where(u => u.RelKey == Define.USERORG && delOrgIds.Contains(u.SecondId)).ExecuteCommand();
|
||||
SugarClient.Deleteable<SysOrg>().Where(u => delOrgIds.Contains(u.Id)).ExecuteCommand();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载特定用户的部门
|
||||
/// 获取所有机构
|
||||
/// </summary>
|
||||
/// <param name="userId">The user unique identifier.</param>
|
||||
public List<SysOrg> LoadForUser(string userId)
|
||||
/// <returns></returns>
|
||||
public List<OrgView> LoadAll()
|
||||
{
|
||||
var result = from userorg in UnitWork.Find<Relevance>(null)
|
||||
join org in UnitWork.Find<SysOrg>(null) on userorg.SecondId equals org.Id
|
||||
where userorg.FirstId == userId && userorg.RelKey == Define.USERORG
|
||||
select org;
|
||||
return result.ToList();
|
||||
return SugarClient.Queryable<SysOrg>()
|
||||
.LeftJoin<SysUser>((org, user) => org.ChairmanId ==user.Id)
|
||||
.Select((org,user)=>new OrgView
|
||||
{
|
||||
Id = org.Id.SelectAll(),
|
||||
ChairmanName = user.Name
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public OrgManagerApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<SysOrg,OpenAuthDBContext> repository,IAuth auth,
|
||||
RevelanceManagerApp revelanceApp) : base(unitWork, repository, auth)
|
||||
public OrgManagerApp(ISqlSugarClient client, IAuth auth,
|
||||
RevelanceManagerApp revelanceApp) : base(client, auth)
|
||||
{
|
||||
_revelanceApp = revelanceApp;
|
||||
}
|
||||
|
||||
public string[] GetChairmanId(string[] orgIds)
|
||||
{
|
||||
return Repository.Find(u => orgIds.Contains(u.Id)&&u.ChairmanId!= null).Select(u => u.ChairmanId).ToArray();
|
||||
return SugarClient.Queryable<SysOrg>().Where(u => orgIds.Contains(u.Id) && u.ChairmanId != null).Select(u => u.ChairmanId).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Helpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
@@ -44,7 +45,7 @@ namespace OpenAuth.App
|
||||
RelKey = key,
|
||||
FirstId = sameVals.Key,
|
||||
SecondId = value,
|
||||
OperateTime = DateTime.Now
|
||||
OperateTime = TimeHelper.Now
|
||||
}).ToArray());
|
||||
UnitWork.Save();
|
||||
}
|
||||
@@ -152,7 +153,7 @@ namespace OpenAuth.App
|
||||
FirstId = request.RoleId,
|
||||
SecondId = request.ModuleCode,
|
||||
ThirdId = requestProperty,
|
||||
OperateTime = DateTime.Now
|
||||
OperateTime = TimeHelper.Now
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ namespace OpenAuth.App
|
||||
RelKey = Define.USERROLE,
|
||||
FirstId = firstId,
|
||||
SecondId = request.RoleId,
|
||||
OperateTime = DateTime.Now
|
||||
OperateTime = TimeHelper.Now
|
||||
}).ToArray());
|
||||
UnitWork.Save();
|
||||
});
|
||||
@@ -229,7 +230,7 @@ namespace OpenAuth.App
|
||||
RelKey = Define.USERORG,
|
||||
FirstId = firstId,
|
||||
SecondId = request.OrgId,
|
||||
OperateTime = DateTime.Now
|
||||
OperateTime = TimeHelper.Now
|
||||
}).ToArray());
|
||||
UnitWork.Save();
|
||||
});
|
||||
@@ -252,7 +253,7 @@ namespace OpenAuth.App
|
||||
RelKey = Define.ROLERESOURCE,
|
||||
FirstId = request.RoleId,
|
||||
SecondId = firstId,
|
||||
OperateTime = DateTime.Now
|
||||
OperateTime = TimeHelper.Now
|
||||
}).ToArray());
|
||||
UnitWork.Save();
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using SqlSugar;
|
||||
using Infrastructure.Helpers;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
@@ -34,7 +35,7 @@ namespace OpenAuth.App
|
||||
{
|
||||
var obj = resource.MapTo<SysResource>();
|
||||
CaculateCascade(obj);
|
||||
obj.CreateTime = DateTime.Now;
|
||||
obj.CreateTime = TimeHelper.Now;
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.CreateUserId = user.Id;
|
||||
obj.CreateUserName = user.Name;
|
||||
@@ -56,7 +57,7 @@ namespace OpenAuth.App
|
||||
TypeId = obj.TypeId,
|
||||
TypeName = obj.TypeName,
|
||||
Description = obj.Description,
|
||||
UpdateTime = DateTime.Now,
|
||||
UpdateTime = TimeHelper.Now,
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name
|
||||
//todo:要修改的字段赋值
|
||||
@@ -68,6 +69,11 @@ namespace OpenAuth.App
|
||||
var elementIds = _revelanceApp.Get(Define.ROLERESOURCE, true, roleId);
|
||||
return SugarClient.Queryable<SysResource>().Where(u => elementIds.Contains(u.Id) && (appId == null || appId =="" || u.AppId == appId)).ToArray();
|
||||
}
|
||||
|
||||
public List<SysResource> LoadByIds(string[] ids)
|
||||
{
|
||||
return SugarClient.Queryable<SysResource>().Where(u => ids.Contains(u.Id)).ToList();
|
||||
}
|
||||
|
||||
public async Task<PagedDynamicDataResp> Load(QueryResourcesReq request)
|
||||
{
|
||||
@@ -152,7 +158,7 @@ namespace OpenAuth.App
|
||||
TypeId = Define.API,
|
||||
TypeName = "API接口",
|
||||
Description = api.Summary??"",
|
||||
CreateTime = DateTime.Now,
|
||||
CreateTime = TimeHelper.Now,
|
||||
CreateUserId = user.Id,
|
||||
CreateUserName = user.Name
|
||||
};
|
||||
|
||||
@@ -10,10 +10,12 @@ using Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.Repository;
|
||||
using Infrastructure.Helpers;
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class RoleApp : BaseStringApp<Role,OpenAuthDBContext>
|
||||
public class RoleApp : SqlSugarBaseApp<Role>
|
||||
{
|
||||
private RevelanceManagerApp _revelanceApp;
|
||||
|
||||
@@ -39,7 +41,7 @@ namespace OpenAuth.App
|
||||
public async Task<PagedListDataResp<Role>> LoadAll(QueryRoleListReq request)
|
||||
{
|
||||
var result = new PagedListDataResp<Role>();
|
||||
var objs = UnitWork.Find<Role>(null);
|
||||
var objs = SugarClient.Queryable<Role>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
@@ -59,7 +61,7 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public List<Role> LoadByIds(string[] ids)
|
||||
{
|
||||
return UnitWork.Find<Role>(u => ids.Contains(u.Id)).ToList();
|
||||
return Repository.GetList(u => ids.Contains(u.Id));
|
||||
}
|
||||
|
||||
|
||||
@@ -68,41 +70,38 @@ namespace OpenAuth.App
|
||||
/// </summary>
|
||||
public void Add(RoleView obj)
|
||||
{
|
||||
UnitWork.ExecuteWithTransaction(() =>
|
||||
{
|
||||
Role role = obj;
|
||||
role.CreateTime = DateTime.Now;
|
||||
UnitWork.Add(role);
|
||||
UnitWork.Save();
|
||||
obj.Id = role.Id; //要把保存后的ID存入view
|
||||
|
||||
//如果当前账号不是SYSTEM,则直接分配
|
||||
var loginUser = _auth.GetCurrentUser();
|
||||
if (loginUser.User.Account != Define.SYSTEM_USERNAME)
|
||||
{
|
||||
_revelanceApp.Assign(new AssignReq
|
||||
{
|
||||
type = Define.USERROLE,
|
||||
firstId = loginUser.User.Id,
|
||||
secIds = new[] {role.Id}
|
||||
});
|
||||
}
|
||||
});
|
||||
SugarClient.Ado.BeginTran();
|
||||
Role role = obj;
|
||||
role.CreateTime = TimeHelper.Now;
|
||||
Repository.Insert(role);
|
||||
obj.Id = role.Id; //要把保存后的ID存入view
|
||||
|
||||
//如果当前账号不是SYSTEM,则直接分配
|
||||
var loginUser = _auth.GetCurrentUser();
|
||||
if (loginUser.User.Account != Define.SYSTEM_USERNAME)
|
||||
{
|
||||
_revelanceApp.Assign(new AssignReq
|
||||
{
|
||||
type = Define.USERROLE,
|
||||
firstId = loginUser.User.Id,
|
||||
secIds = new[] {role.Id}
|
||||
});
|
||||
}
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除角色时,需要删除角色对应的权限
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
public override void Delete(string[] ids)
|
||||
public new void Delete(string[] ids)
|
||||
{
|
||||
UnitWork.ExecuteWithTransaction(() =>
|
||||
{
|
||||
UnitWork.Delete<Relevance>(u=>(u.RelKey == Define.ROLEMODULE || u.RelKey == Define.ROLEELEMENT) && ids.Contains(u.FirstId));
|
||||
UnitWork.Delete<Relevance>(u=>u.RelKey == Define.USERROLE && ids.Contains(u.SecondId));
|
||||
UnitWork.Delete<Role>(u =>ids.Contains(u.Id));
|
||||
UnitWork.Save();
|
||||
});
|
||||
SugarClient.Ado.BeginTran();
|
||||
SugarClient.Deleteable<Relevance>().Where(u=>(u.RelKey == Define.ROLEMODULE || u.RelKey == Define.ROLEELEMENT) && ids.Contains(u.FirstId)).ExecuteCommand();
|
||||
SugarClient.Deleteable<Relevance>().Where(u=>u.RelKey == Define.USERROLE && ids.Contains(u.SecondId)).ExecuteCommand();
|
||||
SugarClient.Deleteable<Role>().Where(u =>ids.Contains(u.Id)).ExecuteCommand();
|
||||
SugarClient.Ado.CommitTran();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -111,19 +110,16 @@ namespace OpenAuth.App
|
||||
/// <param name="obj"></param>
|
||||
public void Update(RoleView obj)
|
||||
{
|
||||
Role role = obj;
|
||||
|
||||
UnitWork.Update<Role>(u => u.Id == obj.Id, u => new Role
|
||||
Repository.Update(u => new Role
|
||||
{
|
||||
Name = role.Name,
|
||||
Status = role.Status
|
||||
});
|
||||
|
||||
Name = obj.Name,
|
||||
Status = obj.Status
|
||||
}, u => u.Id == obj.Id);
|
||||
}
|
||||
|
||||
|
||||
public RoleApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<Role,OpenAuthDBContext> repository,
|
||||
RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth)
|
||||
public RoleApp(ISqlSugarClient client,
|
||||
RevelanceManagerApp app,IAuth auth) : base(client, auth)
|
||||
{
|
||||
_revelanceApp = app;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Cache;
|
||||
using Infrastructure.Helpers;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
@@ -76,12 +77,12 @@ namespace OpenAuth.App.SSO
|
||||
Name = sysUserInfo.Name,
|
||||
Token = Guid.NewGuid().ToString().GetHashCode().ToString("x"),
|
||||
AppKey = model.AppKey,
|
||||
CreateTime = DateTime.Now
|
||||
CreateTime = TimeHelper.Now
|
||||
// , IpAddress = HttpContext.Current.Request.UserHostAddress
|
||||
};
|
||||
|
||||
//创建Session
|
||||
_cacheContext.Set(currentSession.Token, currentSession, DateTime.Now.AddDays(10));
|
||||
_cacheContext.Set(currentSession.Token, currentSession, TimeHelper.Now.AddDays(10));
|
||||
|
||||
result.Code = 200;
|
||||
result.Token = currentSession.Token;
|
||||
|
||||
@@ -5,9 +5,15 @@ namespace OpenAuth.App.SSO
|
||||
|
||||
public class PassportLoginRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录账号
|
||||
/// </summary>
|
||||
/// <example>System</example>
|
||||
public string Account { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录密码
|
||||
/// </summary>
|
||||
/// <example>123456</example>
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
using Infrastructure.Helpers;
|
||||
|
||||
|
||||
namespace OpenAuth.App
|
||||
@@ -90,7 +91,7 @@ namespace OpenAuth.App
|
||||
FromId = Guid.Empty.ToString(),
|
||||
FromName = "系统管理员",
|
||||
Content = message,
|
||||
CreateTime = DateTime.Now
|
||||
CreateTime = TimeHelper.Now
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,15 @@ namespace OpenAuth.App.Test
|
||||
Console.WriteLine($"添加数据库连接: {conn.Key} / {(dbtypes.ContainsKey(conn.Key) ? dbtypes[conn.Key] : "未指定类型")},连接字符串:{conn.Value}");
|
||||
}
|
||||
|
||||
//通过ConfigId为空判断是否有默认的连接字符串
|
||||
if(!connectionConfigs.Any(x => x.ConfigId == null))
|
||||
{
|
||||
throw new Exception($"没有找到默认的连接字符串:{Define.DEFAULT_TENANT_ID}");
|
||||
}
|
||||
|
||||
//把connectionConfigs排序,ConfigId为空的放在最前面,即默认的连接字符串必须排最前面
|
||||
connectionConfigs = connectionConfigs.OrderBy(x => x.ConfigId == null ? 0 : 1).ToList();
|
||||
|
||||
var sqlSugar = new SqlSugarClient(connectionConfigs);
|
||||
|
||||
// 配置PostgreSQL数据库处理
|
||||
|
||||
@@ -12,100 +12,120 @@ using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
using SqlSugar;
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class UserManagerApp : BaseStringApp<SysUser,OpenAuthDBContext>
|
||||
public class UserManagerApp : SqlSugarBaseApp<SysUser>
|
||||
{
|
||||
private RevelanceManagerApp _revelanceApp;
|
||||
private OrgManagerApp _orgManagerApp;
|
||||
public UserManagerApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<SysUser,OpenAuthDBContext> repository,
|
||||
RevelanceManagerApp app,IAuth auth, OrgManagerApp orgManagerApp) : base(unitWork, repository, auth)
|
||||
public UserManagerApp(ISqlSugarClient client,
|
||||
RevelanceManagerApp app, IAuth auth, OrgManagerApp orgManagerApp) : base(client, auth)
|
||||
{
|
||||
_revelanceApp = app;
|
||||
_orgManagerApp = orgManagerApp;
|
||||
}
|
||||
public SysUser GetByAccount(string account)
|
||||
{
|
||||
return Repository.FirstOrDefault(u => u.Account == account);
|
||||
return SugarClient.Queryable<SysUser>().First(u => u.Account == account);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载用户列表
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="ignoreAuth">是否忽略权限,如果忽略权限,可以获取到所有用户</param>
|
||||
/// <returns></returns>
|
||||
private async Task<PagedListDataResp<UserView>> LoadUsers(QueryUserListReq request, bool ignoreAuth = false)
|
||||
{
|
||||
var loginUser = _auth.GetCurrentUser();
|
||||
var query = SugarClient.Queryable<SysUser>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
query = query.Where(u => u.Name.Contains(request.key) || u.Account.Contains(request.key));
|
||||
}
|
||||
|
||||
var orgs = SugarClient.Queryable<SysOrg>();
|
||||
if(!ignoreAuth) //如果没有忽略权限,则只能访问自己所在的机构
|
||||
{
|
||||
var orgIds = loginUser.Orgs.Select(u => u.Id).ToArray();
|
||||
orgs = orgs.Where(u => orgIds.Contains(u.Id));
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(request.orgId)) //如果请求的orgId不为空,加载这个机构及该机构下级的所有用户
|
||||
{
|
||||
var reqorg = SugarClient.Queryable<SysOrg>().First(u => u.Id == request.orgId);
|
||||
var cascadeId = reqorg.CascadeId;
|
||||
var orgIds = orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray();
|
||||
var userIds = SugarClient.Queryable<Relevance>().Where(r => r.RelKey == Define.USERORG
|
||||
&& orgIds.Contains(r.SecondId)).Select(r => r.FirstId).Distinct().ToList();
|
||||
query = query.Where(u => userIds.Contains(u.Id));
|
||||
|
||||
}else{
|
||||
if(!ignoreAuth) //如果没有忽略权限,则根据用户所在的机构获取用户
|
||||
{
|
||||
var orgIds = orgs.Select(o => o.Id).ToArray();
|
||||
var userIds = SugarClient.Queryable<Relevance>().Where(r => r.RelKey == Define.USERORG
|
||||
&& orgIds.Contains(r.SecondId)).Select(r => r.FirstId).Distinct().ToList();
|
||||
query = query.Where(u => userIds.Contains(u.Id));
|
||||
}
|
||||
|
||||
//没有限制权限、没有传入orgId,则query就是获取最原始的所有用户
|
||||
}
|
||||
|
||||
var userOrgs = query
|
||||
.LeftJoin<SysUser>((user, u) => user.ParentId == u.Id)
|
||||
.LeftJoin<Relevance>((user, u, r) => user.Id == r.FirstId && r.RelKey == Define.USERORG)
|
||||
.LeftJoin<SysOrg>((user, u, r, o) => r.SecondId == o.Id);
|
||||
|
||||
var userOrgsResult = userOrgs.Select((user, u, r, o) => new
|
||||
{
|
||||
Account = user.Account,
|
||||
Name = user.Name,
|
||||
Id = user.Id,
|
||||
Sex = user.Sex,
|
||||
Status = user.Status,
|
||||
BizCode = user.BizCode,
|
||||
CreateId = user.CreateId,
|
||||
CreateTime = user.CreateTime,
|
||||
TypeId = user.TypeId,
|
||||
TypeName = user.TypeName,
|
||||
ParentId = user.ParentId,
|
||||
ParentName = u.Name,
|
||||
Key = r.RelKey,
|
||||
OrgId = o.Id,
|
||||
OrgName = o.Name
|
||||
});
|
||||
var userViews = (await userOrgsResult.ToListAsync()).GroupBy(b => b.Account)
|
||||
.Select(u => new UserView
|
||||
{
|
||||
Id = u.First().Id,
|
||||
Account = u.Key,
|
||||
Name = u.First().Name,
|
||||
ParentName = u.First().ParentName,
|
||||
Sex = u.First().Sex,
|
||||
Status = u.First().Status,
|
||||
ParentId = u.First().ParentId,
|
||||
CreateTime = u.First().CreateTime,
|
||||
CreateUser = u.First().CreateId,
|
||||
OrganizationIds = string.Join(",", u.Select(x => x.OrgId)),
|
||||
Organizations = string.Join(",", u.Select(x => x.OrgName))
|
||||
});
|
||||
return new PagedListDataResp<UserView>
|
||||
{
|
||||
Count = userViews.Count(),
|
||||
Data = userViews.OrderBy(u => u.Name)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).ToList(),
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// 加载当前登录用户可访问的一个部门及子部门全部用户
|
||||
/// 如果请求的request.OrgId为空,则可以获取到已被删除机构的用户(即:没有分配任何机构的用户)
|
||||
/// </summary>
|
||||
public async Task<PagedDynamicDataResp> Load(QueryUserListReq request)
|
||||
public async Task<PagedListDataResp<UserView>> Load(QueryUserListReq request)
|
||||
{
|
||||
var loginUser = _auth.GetCurrentUser();
|
||||
IQueryable<SysUser> query = UnitWork.Find<SysUser>(null);
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
query = UnitWork.Find<SysUser>(u => u.Name.Contains(request.key) || u.Account.Contains(request.key));
|
||||
}
|
||||
var userOrgs = from user in query
|
||||
join user2 in UnitWork.Find<SysUser>(null)
|
||||
on user.ParentId equals user2.Id into tempuser
|
||||
from u in tempuser.DefaultIfEmpty()
|
||||
join relevance in UnitWork.Find<Relevance>(u => u.RelKey == "UserOrg")
|
||||
on user.Id equals relevance.FirstId into temp
|
||||
from r in temp.DefaultIfEmpty()
|
||||
join org in UnitWork.Find<SysOrg>(null)
|
||||
on r.SecondId equals org.Id into orgtmp
|
||||
from o in orgtmp.DefaultIfEmpty()
|
||||
select new
|
||||
{
|
||||
user.Account,
|
||||
user.Name,
|
||||
user.Id,
|
||||
user.Sex,
|
||||
user.Status,
|
||||
user.BizCode,
|
||||
user.CreateId,
|
||||
user.CreateTime,
|
||||
user.TypeId,
|
||||
user.TypeName,
|
||||
user.ParentId,
|
||||
ParentName = u.Name, //直属上级
|
||||
Key = r.RelKey,
|
||||
r.SecondId,
|
||||
OrgId = o.Id,
|
||||
OrgName = o.Name
|
||||
};
|
||||
//如果请求的orgId不为空
|
||||
if (!string.IsNullOrEmpty(request.orgId))
|
||||
{
|
||||
var org = loginUser.Orgs.SingleOrDefault(u => u.Id == request.orgId);
|
||||
var cascadeId = org.CascadeId;
|
||||
var orgIds = loginUser.Orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray();
|
||||
//只获取机构里面的用户
|
||||
userOrgs = userOrgs.Where(u => u.Key == Define.USERORG && orgIds.Contains(u.OrgId));
|
||||
}
|
||||
else //todo:如果请求的orgId为空,即为跟节点,这时可以额外获取到机构已经被删除的用户,从而进行机构分配。可以根据自己需求进行调整
|
||||
{
|
||||
var orgIds = loginUser.Orgs.Select(u => u.Id).ToArray();
|
||||
//获取用户可以访问的机构的用户和没有任何机构关联的用户(机构被删除后,没有删除这里面的关联关系)
|
||||
userOrgs = userOrgs.Where(u => (u.Key == Define.USERORG && orgIds.Contains(u.OrgId)) || (u.OrgId == null));
|
||||
}
|
||||
var userViews = (await userOrgs.ToListAsync()).GroupBy(b => b.Account)
|
||||
.Select(u =>new UserView
|
||||
{
|
||||
Id = u.First().Id,
|
||||
Account = u.Key,
|
||||
Name = u.First().Name,
|
||||
ParentName = u.First().ParentName,
|
||||
Sex = u.First().Sex,
|
||||
Status = u.First().Status,
|
||||
ParentId = u.First().ParentId,
|
||||
CreateTime = u.First().CreateTime,
|
||||
CreateUser = u.First().CreateId,
|
||||
OrganizationIds = string.Join(",", u.Select(x=>x.OrgId))
|
||||
,Organizations = string.Join(",", u.Select(x=>x.OrgName))
|
||||
});
|
||||
return new PagedDynamicDataResp
|
||||
{
|
||||
Count =userViews.Count(),
|
||||
Data =userViews.OrderBy(u => u.Name)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit),
|
||||
};
|
||||
return await LoadUsers(request, false);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取所有的用户
|
||||
@@ -113,128 +133,66 @@ namespace OpenAuth.App
|
||||
/// </summary>
|
||||
public async Task<PagedListDataResp<UserView>> LoadAll(QueryUserListReq request)
|
||||
{
|
||||
IQueryable<SysUser> query = UnitWork.Find<SysUser>(null);
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
query = UnitWork.Find<SysUser>(u => u.Name.Contains(request.key) || u.Account.Contains(request.key));
|
||||
}
|
||||
var userOrgs = from user in query
|
||||
join user2 in UnitWork.Find<SysUser>(null)
|
||||
on user.ParentId equals user2.Id into tempuser
|
||||
from u in tempuser.DefaultIfEmpty()
|
||||
join relevance in UnitWork.Find<Relevance>(u => u.RelKey == "UserOrg")
|
||||
on user.Id equals relevance.FirstId into temp
|
||||
from r in temp.DefaultIfEmpty()
|
||||
join org in UnitWork.Find<SysOrg>(null)
|
||||
on r.SecondId equals org.Id into orgtmp
|
||||
from o in orgtmp.DefaultIfEmpty()
|
||||
select new
|
||||
{
|
||||
user.Account,
|
||||
user.Name,
|
||||
user.Id,
|
||||
user.Sex,
|
||||
user.Status,
|
||||
user.BizCode,
|
||||
user.CreateId,
|
||||
user.CreateTime,
|
||||
user.TypeId,
|
||||
user.TypeName,
|
||||
user.ParentId,
|
||||
ParentName = u.Name, //直属上级
|
||||
Key = r.RelKey,
|
||||
r.SecondId,
|
||||
OrgId = o.Id,
|
||||
OrgName = o.Name
|
||||
};
|
||||
//如果请求的orgId不为空
|
||||
if (!string.IsNullOrEmpty(request.orgId))
|
||||
{
|
||||
userOrgs = userOrgs.Where(u => u.Key == Define.USERORG && u.OrgId == request.orgId);
|
||||
}
|
||||
var userViews = (await userOrgs.ToListAsync()).GroupBy(b => b.Account).Select(u =>new UserView
|
||||
{
|
||||
Id = u.First().Id,
|
||||
Account = u.Key,
|
||||
Name = u.First().Name,
|
||||
Sex = u.First().Sex,
|
||||
Status = u.First().Status,
|
||||
CreateTime = u.First().CreateTime,
|
||||
CreateUser = u.First().CreateId,
|
||||
ParentName = u.First().ParentName,
|
||||
ParentId = u.First().ParentId,
|
||||
OrganizationIds = string.Join(",", u.Select(x=>x.OrgId))
|
||||
,Organizations = string.Join(",", u.Select(x=>x.OrgName))
|
||||
});
|
||||
return new PagedListDataResp<UserView>()
|
||||
{
|
||||
Count = userViews.Count(),
|
||||
Data = userViews.OrderBy(u => u.Name)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).ToList()
|
||||
};
|
||||
return await LoadUsers(request, true);
|
||||
}
|
||||
public void AddOrUpdate(UpdateUserReq request)
|
||||
{
|
||||
request.ValidationEntity(u => new {u.Account,u.Name, u.OrganizationIds});
|
||||
request.ValidationEntity(u => new { u.Account, u.Name, u.OrganizationIds });
|
||||
if (string.IsNullOrEmpty(request.OrganizationIds))
|
||||
throw new Exception("请为用户分配机构");
|
||||
SysUser requser = request;
|
||||
requser.CreateId = _auth.GetCurrentUser().User.Id;
|
||||
UnitWork.ExecuteWithTransaction(() =>
|
||||
SugarClient.Ado.BeginTran();
|
||||
if (string.IsNullOrEmpty(request.Id))
|
||||
{
|
||||
if (string.IsNullOrEmpty(request.Id))
|
||||
if (SugarClient.Queryable<SysUser>().Any(u => u.Account == request.Account))
|
||||
{
|
||||
if (UnitWork.Any<SysUser>(u => u.Account == request.Account))
|
||||
{
|
||||
throw new Exception("用户账号已存在");
|
||||
}
|
||||
if (string.IsNullOrEmpty(requser.Password))
|
||||
{
|
||||
requser.Password = requser.Account; //如果客户端没提供密码,默认密码同账号
|
||||
}
|
||||
requser.CreateTime = DateTime.Now;
|
||||
UnitWork.Add(requser);
|
||||
request.Id = requser.Id; //要把保存后的ID存入view
|
||||
throw new Exception("用户账号已存在");
|
||||
}
|
||||
else
|
||||
if (string.IsNullOrEmpty(requser.Password))
|
||||
{
|
||||
UnitWork.Update<SysUser>(u => u.Id == request.Id, u => new SysUser
|
||||
{
|
||||
Account = requser.Account,
|
||||
BizCode = requser.BizCode,
|
||||
Name = requser.Name,
|
||||
Sex = requser.Sex,
|
||||
Status = requser.Status,
|
||||
ParentId = request.ParentId
|
||||
});
|
||||
if (!string.IsNullOrEmpty(requser.Password)) //密码为空的时候,不做修改
|
||||
{
|
||||
UnitWork.Update<SysUser>(u => u.Id == request.Id, u => new SysUser
|
||||
{
|
||||
Password = requser.Password
|
||||
});
|
||||
}
|
||||
requser.Password = requser.Account; //如果客户端没提供密码,默认密码同账号
|
||||
}
|
||||
UnitWork.Save();
|
||||
string[] orgIds = request.OrganizationIds.Split(',').ToArray();
|
||||
_revelanceApp.DeleteBy(Define.USERORG, requser.Id);
|
||||
_revelanceApp.Assign(Define.USERORG, orgIds.ToLookup(u => requser.Id));
|
||||
});
|
||||
requser.CreateTime = DateTime.Now;
|
||||
Repository.Insert(requser);
|
||||
request.Id = requser.Id; //要把保存后的ID存入view
|
||||
}
|
||||
else
|
||||
{
|
||||
Repository.Update(u => new SysUser
|
||||
{
|
||||
Account = requser.Account,
|
||||
BizCode = requser.BizCode,
|
||||
Name = requser.Name,
|
||||
Sex = requser.Sex,
|
||||
Status = requser.Status,
|
||||
ParentId = request.ParentId
|
||||
}, u => u.Id == request.Id);
|
||||
if (!string.IsNullOrEmpty(requser.Password)) //密码为空的时候,不做修改
|
||||
{
|
||||
Repository.Update(u => new SysUser
|
||||
{
|
||||
Password = requser.Password
|
||||
}, u => u.Id == request.Id);
|
||||
}
|
||||
}
|
||||
string[] orgIds = request.OrganizationIds.Split(',').ToArray();
|
||||
_revelanceApp.DeleteBy(Define.USERORG, requser.Id);
|
||||
_revelanceApp.Assign(Define.USERORG, orgIds.ToLookup(u => requser.Id));
|
||||
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
/// <summary>
|
||||
/// 删除用户,包含用户与组织关系、用户与角色关系
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
public override void Delete(string[] ids)
|
||||
public new void Delete(string[] ids)
|
||||
{
|
||||
UnitWork.ExecuteWithTransaction(() =>
|
||||
{
|
||||
UnitWork.Delete<Relevance>(u =>(u.RelKey == Define.USERROLE || u.RelKey == Define.USERORG)
|
||||
&& ids.Contains(u.FirstId));
|
||||
UnitWork.Delete<SysUser>(u => ids.Contains(u.Id));
|
||||
UnitWork.Save();
|
||||
});
|
||||
SugarClient.Ado.BeginTran();
|
||||
SugarClient.Deleteable<Relevance>().Where(u => (u.RelKey == Define.USERROLE || u.RelKey == Define.USERORG)
|
||||
&& ids.Contains(u.FirstId)).ExecuteCommand();
|
||||
SugarClient.Deleteable<SysUser>().Where(u => ids.Contains(u.Id)).ExecuteCommand();
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
/// <summary>
|
||||
/// 修改密码
|
||||
@@ -242,10 +200,10 @@ namespace OpenAuth.App
|
||||
/// <param name="request"></param>
|
||||
public void ChangePassword(ChangePasswordReq request)
|
||||
{
|
||||
Repository.Update(u => u.Account == request.Account, user => new SysUser
|
||||
Repository.Update(u => new SysUser
|
||||
{
|
||||
Password = request.Password
|
||||
});
|
||||
}, u => u.Account == request.Account);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取指定角色包含的用户列表
|
||||
@@ -254,15 +212,15 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public async Task<PagedDynamicDataResp> LoadByRole(QueryUserListByRoleReq request)
|
||||
{
|
||||
var users = from userRole in UnitWork.Find<Relevance>(u =>
|
||||
u.SecondId == request.roleId && u.RelKey == Define.USERROLE)
|
||||
join user in UnitWork.Find<SysUser>(null) on userRole.FirstId equals user.Id into temp
|
||||
from c in temp.Where(u =>u.Id != null)
|
||||
select c;
|
||||
var users = SugarClient.Queryable<Relevance>()
|
||||
.Where(u => u.SecondId == request.roleId && u.RelKey == Define.USERROLE)
|
||||
.LeftJoin<SysUser>((userRole, user) => userRole.FirstId == user.Id)
|
||||
.Where((userRole, user) => user.Id != null)
|
||||
.Select((userRole, user) => user);
|
||||
return new PagedDynamicDataResp
|
||||
{
|
||||
Count =await users.CountAsync(),
|
||||
Data =await users.Skip((request.page - 1) * request.limit).Take(request.limit).ToListAsync()
|
||||
Count = await users.CountAsync(),
|
||||
Data = await users.Skip((request.page - 1) * request.limit).Take(request.limit).ToListAsync()
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
@@ -272,15 +230,15 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public async Task<PagedDynamicDataResp> LoadByOrg(QueryUserListByOrgReq request)
|
||||
{
|
||||
var users = from userOrg in UnitWork.Find<Relevance>(u =>
|
||||
u.SecondId == request.orgId && u.RelKey == Define.USERORG)
|
||||
join user in UnitWork.Find<SysUser>(null) on userOrg.FirstId equals user.Id into temp
|
||||
from c in temp.Where(u =>u.Id != null)
|
||||
select c;
|
||||
var users = SugarClient.Queryable<Relevance>()
|
||||
.Where(u => u.SecondId == request.orgId && u.RelKey == Define.USERORG)
|
||||
.LeftJoin<SysUser>((userOrg, user) => userOrg.FirstId == user.Id)
|
||||
.Where((userOrg, user) => user.Id != null)
|
||||
.Select((userOrg, user) => user);
|
||||
return new PagedDynamicDataResp
|
||||
{
|
||||
Count =await users.CountAsync(),
|
||||
Data =await users.Skip((request.page - 1) * request.limit).Take(request.limit).ToListAsync()
|
||||
Count = await users.CountAsync(),
|
||||
Data = await users.Skip((request.page - 1) * request.limit).Take(request.limit).ToListAsync()
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
@@ -293,13 +251,13 @@ namespace OpenAuth.App
|
||||
{
|
||||
throw new Exception("不能修改超级管理员信息");
|
||||
}
|
||||
Repository.Update(u => u.Account == request.Account, user => new SysUser
|
||||
{
|
||||
Name = request.Name,
|
||||
Sex = request.Sex
|
||||
});
|
||||
Repository.Update(u => new SysUser
|
||||
{
|
||||
Name = request.Name,
|
||||
Sex = request.Sex
|
||||
}, u => u.Account == request.Account);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户的直属上级ID
|
||||
/// </summary>
|
||||
@@ -311,7 +269,7 @@ namespace OpenAuth.App
|
||||
{
|
||||
throw new Exception("超级管理员没有直属上级,请检查配置");
|
||||
}
|
||||
return Repository.FirstOrDefault(u => u.Id == userid).ParentId;
|
||||
return SugarClient.Queryable<SysUser>().First(u => u.Id == userid).ParentId;
|
||||
}
|
||||
|
||||
|
||||
@@ -325,35 +283,31 @@ namespace OpenAuth.App
|
||||
var sql = $@"
|
||||
select u.*
|
||||
from sysuser u
|
||||
join (select distinct SecondId as UserId
|
||||
from Relevance
|
||||
where RelKey = '{Define.INSTANCE_NOTICE_USER}'
|
||||
and FirstId = '{instanceId}'
|
||||
union
|
||||
select distinct FirstId as UserId
|
||||
from Relevance a
|
||||
inner join (select SecondId as RoleId
|
||||
from Relevance
|
||||
where RelKey = '{Define.INSTANCE_NOTICE_ROLE}'
|
||||
and FirstId = '{instanceId}') b on a.SecondId = b.RoleId
|
||||
where RelKey = 'UserRole') userids on u.Id = userids.UserId";
|
||||
var users = UnitWork.FromSql<SysUser>(sql);
|
||||
return users.Select(u=>u.Id).ToList();
|
||||
join (select distinct SecondId as UserId
|
||||
from Relevance
|
||||
where RelKey = '{Define.INSTANCE_NOTICE_USER}'
|
||||
and FirstId = '{instanceId}'
|
||||
union
|
||||
select distinct FirstId as UserId
|
||||
from Relevance a
|
||||
inner join (select SecondId as RoleId
|
||||
from Relevance
|
||||
where RelKey = '{Define.INSTANCE_NOTICE_ROLE}'
|
||||
and FirstId = '{instanceId}') b on a.SecondId = b.RoleId
|
||||
where RelKey = '{Define.USERROLE}') userids on u.Id = userids.UserId";
|
||||
var users = SugarClient.Ado.SqlQuery<SysUser>(sql);
|
||||
return users.Select(u => u.Id).ToList();
|
||||
}
|
||||
|
||||
public List<UserView> LoadByIds(string[] ids)
|
||||
{
|
||||
var users = Repository.Find(u => ids.Contains(u.Id));
|
||||
var users = SugarClient.Queryable<SysUser>().Where(u => ids.Contains(u.Id));
|
||||
|
||||
//获取用户及用户关联的机构
|
||||
var userOrgs = from user in users
|
||||
join relevance in UnitWork.Find<Relevance>(u => u.RelKey == "UserOrg")
|
||||
on user.Id equals relevance.FirstId into temp
|
||||
from r in temp.DefaultIfEmpty()
|
||||
join org in UnitWork.Find<SysOrg>(null)
|
||||
on r.SecondId equals org.Id into orgtmp
|
||||
from o in orgtmp.DefaultIfEmpty()
|
||||
select new
|
||||
var userOrgs = users
|
||||
.LeftJoin<Relevance>((user, r) => user.Id == r.FirstId && r.RelKey == Define.USERORG)
|
||||
.LeftJoin<SysOrg>((user, r, o) => r.SecondId == o.Id)
|
||||
.Select((user, r, o) => new
|
||||
{
|
||||
user.Id,
|
||||
user.Account,
|
||||
@@ -365,21 +319,22 @@ namespace OpenAuth.App
|
||||
user.ParentId,
|
||||
OrgId = o.Id,
|
||||
OrgName = o.Name
|
||||
};
|
||||
});
|
||||
|
||||
var userViews = userOrgs.GroupBy(b => b.Account).Select(u => new UserView
|
||||
var userOrgsList = userOrgs.ToList(); // 先执行ToList()
|
||||
var userViews = userOrgsList.GroupBy(b => b.Account).Select(g => new UserView
|
||||
{
|
||||
Id = u.First().Id,
|
||||
Account = u.Key,
|
||||
Name = u.First().Name,
|
||||
Sex = u.First().Sex,
|
||||
Status = u.First().Status,
|
||||
CreateTime = u.First().CreateTime,
|
||||
CreateUser = u.First().CreateId,
|
||||
ParentName = u.First().ParentId,
|
||||
ParentId = u.First().ParentId,
|
||||
OrganizationIds = string.Join(",", u.Select(x=>x.OrgId))
|
||||
,Organizations = string.Join(",", u.Select(x=>x.OrgName))
|
||||
Id = g.First().Id,
|
||||
Account = g.Key,
|
||||
Name = g.First().Name,
|
||||
Sex = g.First().Sex,
|
||||
Status = g.First().Status,
|
||||
CreateTime = g.First().CreateTime,
|
||||
CreateUser = g.First().CreateId,
|
||||
ParentName = g.First().ParentId,
|
||||
ParentId = g.First().ParentId,
|
||||
OrganizationIds = string.Join(",", g.Select(x => x.OrgId)),
|
||||
Organizations = string.Join(",", g.Select(x => x.OrgName))
|
||||
});
|
||||
|
||||
return userViews.ToList();
|
||||
|
||||
@@ -12,6 +12,7 @@ using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using OpenAuth.Repository.Core;
|
||||
using Infrastructure.Helpers;
|
||||
|
||||
namespace OpenAuth.Repository.Domain
|
||||
{
|
||||
@@ -36,10 +37,10 @@ namespace OpenAuth.Repository.Domain
|
||||
this.DeleteMark= 0;
|
||||
this.Disabled= 0;
|
||||
this.Description= string.Empty;
|
||||
this.CreateDate= DateTime.Now;
|
||||
this.CreateDate= TimeHelper.Now;
|
||||
this.CreateUserId= string.Empty;
|
||||
this.CreateUserName= string.Empty;
|
||||
this.ModifyDate= DateTime.Now;
|
||||
this.ModifyDate= TimeHelper.Now;
|
||||
this.ModifyUserId= string.Empty;
|
||||
this.ModifyUserName= string.Empty;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
using OpenAuth.Repository.Core;
|
||||
using Infrastructure.Helpers;
|
||||
|
||||
namespace OpenAuth.Repository.Domain
|
||||
{
|
||||
@@ -27,19 +28,19 @@ namespace OpenAuth.Repository.Domain
|
||||
this.JobName= string.Empty;
|
||||
this.RunCount= 0;
|
||||
this.ErrorCount= 0;
|
||||
this.NextRunTime= DateTime.Now;
|
||||
this.LastRunTime= DateTime.Now;
|
||||
this.LastErrorTime= DateTime.Now;
|
||||
this.NextRunTime= TimeHelper.Now;
|
||||
this.LastRunTime= TimeHelper.Now;
|
||||
this.LastErrorTime= TimeHelper.Now;
|
||||
this.JobType= 0;
|
||||
this.JobCall= string.Empty;
|
||||
this.JobCallParams= string.Empty;
|
||||
this.Cron= string.Empty;
|
||||
this.Status= 0;
|
||||
this.Remark= string.Empty;
|
||||
this.CreateTime= DateTime.Now;
|
||||
this.CreateTime= TimeHelper.Now;
|
||||
this.CreateUserId= string.Empty;
|
||||
this.CreateUserName= string.Empty;
|
||||
this.UpdateTime= DateTime.Now;
|
||||
this.UpdateTime= TimeHelper.Now;
|
||||
this.UpdateUserId= string.Empty;
|
||||
this.UpdateUserName= string.Empty;
|
||||
this.OrgId= string.Empty;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\OpenAuth.Repository.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\OpenAuth.Repository.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenAuth.App;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenAuth.WebApi.Controllers
|
||||
{
|
||||
@@ -79,6 +81,37 @@ namespace OpenAuth.WebApi.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有机构
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public Response<List<OrgView>> LoadAll()
|
||||
{
|
||||
var result = new Response<List<OrgView>>();
|
||||
try
|
||||
{
|
||||
result.Data = _app.LoadAll();
|
||||
}
|
||||
catch (CommonException ex)
|
||||
{
|
||||
if (ex.Code == Define.INVALID_TOKEN)
|
||||
{
|
||||
result.Code = ex.Code;
|
||||
result.Message = ex.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Code = 500;
|
||||
result.Message = ex.InnerException != null
|
||||
? "OpenAuth.WebAPI数据库访问失败:" + ex.InnerException.Message
|
||||
: "OpenAuth.WebAPI数据库访问失败:" + ex.Message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除选中的部门及所有的子部门
|
||||
|
||||
@@ -70,6 +70,14 @@ namespace OpenAuth.WebApi.Controllers
|
||||
return resp;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public Response<List<SysResource>> LoadByIds([FromBody]string[] ids)
|
||||
{
|
||||
var result = new Response<List<SysResource>>();
|
||||
result.Data = _app.LoadByIds(ids);
|
||||
return result;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public Response<string> Add([FromBody] AddOrUpdateResReq obj)
|
||||
{
|
||||
|
||||
41
OpenAuth.WebApi/Controllers/SystemController.cs
Normal file
41
OpenAuth.WebApi/Controllers/SystemController.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace OpenAuth.WebApi.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统信息相关接口
|
||||
/// </summary>
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class SystemController : ControllerBase
|
||||
{
|
||||
private readonly IHostEnvironment _environment;
|
||||
|
||||
public SystemController(IHostEnvironment environment)
|
||||
{
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统环境信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public IActionResult GetEnvironmentInfo()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
EnvironmentName = _environment.EnvironmentName,
|
||||
IsDevelopment = _environment.IsDevelopment(),
|
||||
IsProduction = _environment.IsProduction(),
|
||||
IsStaging = _environment.IsStaging(),
|
||||
SwaggerEnabled = _environment.IsDevelopment(), // Swagger 只在开发环境启用
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace OpenAuth.WebApi.Controllers
|
||||
/// 获取当前登录用户可访问的一个部门及子部门全部用户
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<PagedDynamicDataResp> Load([FromQuery]QueryUserListReq request)
|
||||
public async Task<PagedListDataResp<UserView>> Load([FromQuery]QueryUserListReq request)
|
||||
{
|
||||
return await _app.Load(request);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<None Remove="log\**" />
|
||||
<None Remove="wwwroot\**" />
|
||||
<EmbeddedResource Include="index.html" />
|
||||
<None Update="swagger\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -61,67 +61,73 @@ namespace OpenAuth.WebApi
|
||||
});
|
||||
}
|
||||
|
||||
// 添加MiniProfiler服务
|
||||
services.AddMiniProfiler(options =>
|
||||
// 只在开发环境中添加MiniProfiler服务
|
||||
if (Environment.IsDevelopment())
|
||||
{
|
||||
// 设定访问分析结果URL的路由基地址
|
||||
options.RouteBasePath = "/profiler";
|
||||
services.AddMiniProfiler(options =>
|
||||
{
|
||||
// 设定访问分析结果URL的路由基地址
|
||||
options.RouteBasePath = "/profiler";
|
||||
|
||||
options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto;
|
||||
options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft;
|
||||
options.PopupShowTimeWithChildren = true;
|
||||
options.PopupShowTrivial = true;
|
||||
options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
|
||||
// options.IgnoredPaths.Add("/swagger/");
|
||||
}).AddEntityFramework(); //显示SQL语句及耗时
|
||||
options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto;
|
||||
options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft;
|
||||
options.PopupShowTimeWithChildren = true;
|
||||
options.PopupShowTrivial = true;
|
||||
options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
|
||||
// options.IgnoredPaths.Add("/swagger/");
|
||||
}).AddEntityFramework(); //显示SQL语句及耗时
|
||||
}
|
||||
|
||||
//添加swagger
|
||||
services.AddSwaggerGen(option =>
|
||||
// 只在开发环境中添加swagger
|
||||
if (Environment.IsDevelopment())
|
||||
{
|
||||
foreach (var controller in GetControllers())
|
||||
services.AddSwaggerGen(option =>
|
||||
{
|
||||
var groupname = GetSwaggerGroupName(controller);
|
||||
|
||||
option.SwaggerDoc(groupname, new OpenApiInfo
|
||||
foreach (var controller in GetControllers())
|
||||
{
|
||||
Version = "v1",
|
||||
Title = groupname,
|
||||
Description = "by yubaolee"
|
||||
});
|
||||
}
|
||||
var groupname = GetSwaggerGroupName(controller);
|
||||
|
||||
logger.LogInformation($"api doc basepath:{AppContext.BaseDirectory}");
|
||||
foreach (var name in Directory.GetFiles(AppContext.BaseDirectory, "*.*",
|
||||
SearchOption.AllDirectories).Where(f => Path.GetExtension(f).ToLower() == ".xml"))
|
||||
{
|
||||
option.IncludeXmlComments(name, includeControllerXmlComments: true);
|
||||
// logger.LogInformation($"find api file{name}");
|
||||
}
|
||||
|
||||
option.OperationFilter<GlobalHttpHeaderOperationFilter>(); // 添加httpHeader参数
|
||||
|
||||
if (!string.IsNullOrEmpty(identityServer))
|
||||
{
|
||||
//接入identityserver
|
||||
option.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||
{
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Description = "OAuth2登陆授权",
|
||||
Flows = new OpenApiOAuthFlows
|
||||
option.SwaggerDoc(groupname, new OpenApiInfo
|
||||
{
|
||||
Implicit = new OpenApiOAuthFlow
|
||||
Version = "v1",
|
||||
Title = groupname,
|
||||
Description = "by yubaolee"
|
||||
});
|
||||
}
|
||||
|
||||
logger.LogInformation($"api doc basepath:{AppContext.BaseDirectory}");
|
||||
foreach (var name in Directory.GetFiles(AppContext.BaseDirectory, "*.*",
|
||||
SearchOption.AllDirectories).Where(f => Path.GetExtension(f).ToLower() == ".xml"))
|
||||
{
|
||||
option.IncludeXmlComments(name, includeControllerXmlComments: true);
|
||||
// logger.LogInformation($"find api file{name}");
|
||||
}
|
||||
|
||||
option.OperationFilter<GlobalHttpHeaderOperationFilter>(); // 添加httpHeader参数
|
||||
|
||||
if (!string.IsNullOrEmpty(identityServer))
|
||||
{
|
||||
//接入identityserver
|
||||
option.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||
{
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Description = "OAuth2登陆授权",
|
||||
Flows = new OpenApiOAuthFlows
|
||||
{
|
||||
AuthorizationUrl = new Uri($"{identityServer}/connect/authorize"),
|
||||
Scopes = new Dictionary<string, string>
|
||||
Implicit = new OpenApiOAuthFlow
|
||||
{
|
||||
{"openauthapi", "同意openauth.webapi 的访问权限"} //指定客户端请求的api作用域。 如果为空,则客户端无法访问
|
||||
AuthorizationUrl = new Uri($"{identityServer}/connect/authorize"),
|
||||
Scopes = new Dictionary<string, string>
|
||||
{
|
||||
{"openauthapi", "同意openauth.webapi 的访问权限"} //指定客户端请求的api作用域。 如果为空,则客户端无法访问
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
option.OperationFilter<AuthResponsesOperationFilter>();
|
||||
}
|
||||
});
|
||||
});
|
||||
option.OperationFilter<AuthResponsesOperationFilter>();
|
||||
}
|
||||
});
|
||||
}
|
||||
services.Configure<AppSetting>(Configuration.GetSection("AppSetting"));
|
||||
services.AddControllers(option => { option.Filters.Add<OpenAuthFilter>(); })
|
||||
.ConfigureApiBehaviorOptions(options =>
|
||||
@@ -187,6 +193,15 @@ namespace OpenAuth.WebApi
|
||||
connectionConfigs.Add(config);
|
||||
logger.LogInformation($"添加数据库连接: {conn.Key} / {(dbtypes.ContainsKey(conn.Key) ? dbtypes[conn.Key] : "未指定类型")},连接字符串:{conn.Value}");
|
||||
}
|
||||
|
||||
//通过ConfigId为空判断是否有默认的连接字符串
|
||||
if(!connectionConfigs.Any(x => x.ConfigId == null))
|
||||
{
|
||||
throw new Exception($"没有找到默认的连接字符串:{Define.DEFAULT_TENANT_ID}");
|
||||
}
|
||||
|
||||
//把connectionConfigs排序,ConfigId为空的放在最前面,即默认的连接字符串必须排最前面
|
||||
connectionConfigs = connectionConfigs.OrderBy(x => x.ConfigId == null ? 0 : 1).ToList();
|
||||
|
||||
var sqlSugar = new SqlSugarClient(connectionConfigs);
|
||||
|
||||
@@ -232,12 +247,49 @@ namespace OpenAuth.WebApi
|
||||
{
|
||||
loggerFactory.AddLog4Net();
|
||||
|
||||
//todo:测试可以允许任意跨域,正式环境要加权限
|
||||
app.UseCors(builder => builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
|
||||
// 启用日志追踪记录和异常友好提示
|
||||
app.UseLogMiddleware();
|
||||
|
||||
// 只在开发环境中启用 Swagger 和 MiniProfiler
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseMiniProfiler();
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseSwagger();
|
||||
|
||||
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
|
||||
// specifying the Swagger JSON endpoint.
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.IndexStream = () =>
|
||||
IntrospectionExtensions.GetTypeInfo(GetType()).Assembly
|
||||
.GetManifestResourceStream("OpenAuth.WebApi.index.html");
|
||||
|
||||
foreach (var controller in GetControllers())
|
||||
{
|
||||
var groupname = GetSwaggerGroupName(controller);
|
||||
|
||||
c.SwaggerEndpoint($"/swagger/{groupname}/swagger.json", groupname);
|
||||
}
|
||||
|
||||
c.DocExpansion(DocExpansion.List); //默认展开列表
|
||||
c.OAuthClientId("OpenAuth.WebApi"); //oauth客户端名称
|
||||
c.OAuthAppName("开源版webapi认证"); // 描述
|
||||
});
|
||||
}
|
||||
|
||||
app.UseMiniProfiler();
|
||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||
|
||||
//配置ServiceProvider
|
||||
AutofacContainerModule.ConfigServiceProvider(app.ApplicationServices);
|
||||
|
||||
//可以访问根目录下面的静态文件
|
||||
var staticfile = new StaticFileOptions
|
||||
@@ -251,45 +303,9 @@ namespace OpenAuth.WebApi
|
||||
};
|
||||
app.UseStaticFiles(staticfile);
|
||||
|
||||
|
||||
|
||||
//todo:测试可以允许任意跨域,正式环境要加权限
|
||||
app.UseCors(builder => builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
|
||||
// 启用日志追踪记录和异常友好提示
|
||||
app.UseLogMiddleware();
|
||||
|
||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||
|
||||
//配置ServiceProvider
|
||||
AutofacContainerModule.ConfigServiceProvider(app.ApplicationServices);
|
||||
|
||||
|
||||
app.UseSwagger();
|
||||
|
||||
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
|
||||
// specifying the Swagger JSON endpoint.
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.IndexStream = () =>
|
||||
IntrospectionExtensions.GetTypeInfo(GetType()).Assembly
|
||||
.GetManifestResourceStream("OpenAuth.WebApi.index.html");
|
||||
|
||||
foreach (var controller in GetControllers())
|
||||
{
|
||||
var groupname = GetSwaggerGroupName(controller);
|
||||
|
||||
c.SwaggerEndpoint($"/swagger/{groupname}/swagger.json", groupname);
|
||||
}
|
||||
|
||||
c.DocExpansion(DocExpansion.List); //默认展开列表
|
||||
c.OAuthClientId("OpenAuth.WebApi"); //oauth客户端名称
|
||||
c.OAuthAppName("开源版webapi认证"); // 描述
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="app-container flex-item flex-column">
|
||||
<div class="flex-item">
|
||||
<el-card shadow="nerver" class="demo-card fh">
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList" :loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
<pagination v-show="firstTotal > 0" :total="firstTotal" :page.sync="firstQuery.page" :limit.sync="firstQuery.limit" @pagination="handleCurrentChange" />
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -249,7 +249,7 @@ export default {
|
||||
tempData = this.setDetails(tempData)
|
||||
tempData.OrgId = this.defaultorgid
|
||||
{FirstTableName}s.add(tempData).then(() => {
|
||||
this.mainList.unshift(this.firstTemp)
|
||||
this.getList()
|
||||
this.editModel = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
@@ -289,13 +289,7 @@ export default {
|
||||
let tempData = Object.assign({}, this.firstTemp)
|
||||
tempData = this.setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of this.mainList) {
|
||||
if (v.id === this.firstTemp.id) {
|
||||
const index = this.mainList.indexOf(v)
|
||||
this.mainList.splice(index, 1, this.firstTemp)
|
||||
break
|
||||
}
|
||||
}
|
||||
this.getList()
|
||||
|
||||
this.editModel = false
|
||||
this.$notify({
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<div class="search-box">
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{FirstTableName}s_query_conditions" :columns="firstHeaderList"
|
||||
:query-options="firstQuery" @search="handleFilter" class="flex-1" />
|
||||
:query-options="firstQuery" @search="handleFilter" />
|
||||
<column-setting storageName="{FirstTableName}s_column_settings" :columns="firstHeaderList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
@update:columns="handleColumnsUpdate" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="flex-item flex flex-column main-context">
|
||||
<!-- 主表 -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && !showDetailInTable">
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :loading="listLoading" @row-click="rowClickFirstTable"
|
||||
@row-dblclick="rowDblClickFirstTable"></auth-table>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination size="small" background v-show="firstTotal > 0" v-model:currentPage="firstQuery.page"
|
||||
@@ -71,7 +71,7 @@
|
||||
删除
|
||||
</el-button>
|
||||
<column-setting buttonClass="filter-item" storageName="{SecondTableName}s_column_settings"
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" class="ml-2" />
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" />
|
||||
<el-button size="small" :icon="showNotFullScreen ? FullScreen : Close"
|
||||
@click="showNotFullScreen = !showNotFullScreen">
|
||||
{{ showNotFullScreen ? '全屏' : '退出全屏' }}
|
||||
@@ -385,13 +385,7 @@ const updateData = () => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
}
|
||||
getList()
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
ElNotification.success('更新成功')
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<div class="search-box">
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{FirstTableName}s_query_conditions" :columns="firstHeaderList"
|
||||
:query-options="firstQuery" @search="handleFilter" class="flex-1" />
|
||||
:query-options="firstQuery" @search="handleFilter" />
|
||||
<column-setting storageName="{FirstTableName}s_column_settings" :columns="firstHeaderList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
@update:columns="handleColumnsUpdate" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="flex-item flex flex-column main-context">
|
||||
<!-- 主表 -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && !showDetailInTable">
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :loading="listLoading" @row-click="rowClickFirstTable"
|
||||
@row-dblclick="rowDblClickFirstTable"></auth-table>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination size="small" background v-show="firstTotal > 0" v-model:currentPage="firstQuery.page"
|
||||
@@ -71,7 +71,7 @@
|
||||
删除
|
||||
</el-button>
|
||||
<column-setting buttonClass="filter-item" storageName="{SecondTableName}s_column_settings"
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" class="ml-2" />
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" />
|
||||
<el-button size="small" :icon="showNotFullScreen ? FullScreen : Close"
|
||||
@click="showNotFullScreen = !showNotFullScreen">
|
||||
{{ showNotFullScreen ? '全屏' : '退出全屏' }}
|
||||
@@ -374,13 +374,7 @@ const updateData = () => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
}
|
||||
getList()
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
ElNotification.success('更新成功')
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="app-container flex-item flex-column">
|
||||
<div class="flex-item">
|
||||
<el-card shadow="nerver" class="demo-card fh">
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList" :loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
<pagination v-show="firstTotal > 0" :total="firstTotal" :page.sync="firstQuery.page" :limit.sync="firstQuery.limit" @pagination="handleCurrentChange" />
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -246,7 +246,7 @@ export default {
|
||||
tempData = this.setDetails(tempData)
|
||||
tempData.OrgId = this.defaultorgid
|
||||
{FirstTableName}s.add(tempData).then(() => {
|
||||
this.mainList.unshift(this.firstTemp)
|
||||
this.getList()
|
||||
this.editModel = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
@@ -286,14 +286,7 @@ export default {
|
||||
let tempData = Object.assign({}, this.firstTemp)
|
||||
tempData = this.setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of this.mainList) {
|
||||
if (v.id === this.firstTemp.id) {
|
||||
const index = this.mainList.indexOf(v)
|
||||
this.mainList.splice(index, 1, this.firstTemp)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.getList()
|
||||
this.editModel = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace OpenAuth.App
|
||||
//增加筛选条件,如:
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
{ForeignKeyTemplate}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
:select-type="'checkbox'"
|
||||
:table-fields="headerList"
|
||||
:data="list"
|
||||
:v-loading="listLoading"
|
||||
:loading="listLoading"
|
||||
@row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
></auth-table>
|
||||
@@ -180,7 +180,7 @@ export default {
|
||||
// 保存提交
|
||||
this.$refs['dataForm'].validate(() => {
|
||||
{TableName}s.add(this.temp).then(() => {
|
||||
this.list.unshift(this.temp)
|
||||
this.getList()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
@@ -205,13 +205,7 @@ export default {
|
||||
this.$refs['dataForm'].validate(() => {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
{TableName}s.update(tempData).then(() => {
|
||||
for (const v of this.list) {
|
||||
if (v.id === this.temp.id) {
|
||||
const index = this.list.indexOf(v)
|
||||
this.list.splice(index, 1, this.temp)
|
||||
break
|
||||
}
|
||||
}
|
||||
this.getList()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" class="flex-1" />
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" />
|
||||
<column-setting storageName="{TableName}_column_settings" :columns="headerList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
@update:columns="handleColumnsUpdate" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
<auth-table ref="mainTableRef" :select-type="'checkbox'" :table-fields="headerList" :data="list"
|
||||
:v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
:loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<el-pagination v-show="total > 0" :total="total" v-model:currentPage="listQuery.page"
|
||||
v-model:page-size="listQuery.limit" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
@@ -171,7 +171,7 @@ const handleCreate = async function () {
|
||||
const createData = function () {
|
||||
dataFormRef.value.validate(() => {
|
||||
{TableName}s.add(temp).then(() => {
|
||||
list.value.unshift(temp)
|
||||
getList()
|
||||
dialogFormVisible.value = false
|
||||
ElNotification.success('创建成功')
|
||||
})
|
||||
@@ -188,13 +188,7 @@ const updateData = function () {
|
||||
dataFormRef.value.validate(() => {
|
||||
const tempData = Object.assign({}, temp)
|
||||
{TableName}s.update(tempData).then(() => {
|
||||
for (const v of list.value) {
|
||||
if (v.id === temp.id) {
|
||||
const index = list.value.indexOf(v)
|
||||
list.value.splice(index, 1, temp)
|
||||
break
|
||||
}
|
||||
}
|
||||
getList()
|
||||
dialogFormVisible.value = false
|
||||
ElNotification.success('更新成功')
|
||||
})
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" class="flex-1" />
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" />
|
||||
<column-setting storageName="{TableName}_column_settings" :columns="headerList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
@update:columns="handleColumnsUpdate" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
<auth-table ref="mainTableRef" :select-type="'checkbox'" :table-fields="headerList" :data="list"
|
||||
:v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
:loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<el-pagination v-show="total > 0" :total="total" v-model:currentPage="listQuery.page"
|
||||
v-model:page-size="listQuery.limit" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
@@ -172,7 +172,7 @@ const handleCreate = async function () {
|
||||
const createData = function () {
|
||||
dataFormRef.value.validate(() => {
|
||||
{TableName}s.add(temp).then(() => {
|
||||
list.value.unshift(temp)
|
||||
getList()
|
||||
dialogFormVisible.value = false
|
||||
ElNotification.success('创建成功')
|
||||
})
|
||||
@@ -189,13 +189,7 @@ const updateData = function () {
|
||||
dataFormRef.value.validate(() => {
|
||||
const tempData = Object.assign({}, temp)
|
||||
{TableName}s.update(tempData).then(() => {
|
||||
for (const v of list.value) {
|
||||
if (v.id === temp.id) {
|
||||
const index = list.value.indexOf(v)
|
||||
list.value.splice(index, 1, temp)
|
||||
break
|
||||
}
|
||||
}
|
||||
getList()
|
||||
dialogFormVisible.value = false
|
||||
ElNotification.success('更新成功')
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
:select-type="'checkbox'"
|
||||
:table-fields="headerList"
|
||||
:data="list"
|
||||
:v-loading="listLoading"
|
||||
:loading="listLoading"
|
||||
@row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
></auth-table>
|
||||
@@ -181,7 +181,7 @@ export default {
|
||||
// 保存提交
|
||||
this.$refs['dataForm'].validate(() => {
|
||||
{TableName}s.add(this.temp).then(() => {
|
||||
this.list.unshift(this.temp)
|
||||
this.getList()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
@@ -206,13 +206,7 @@ export default {
|
||||
this.$refs['dataForm'].validate(() => {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
{TableName}s.update(tempData).then(() => {
|
||||
for (const v of this.list) {
|
||||
if (v.id === this.temp.id) {
|
||||
const index = this.list.indexOf(v)
|
||||
this.list.splice(index, 1, this.temp)
|
||||
break
|
||||
}
|
||||
}
|
||||
this.getList()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
|
||||
123
OpenAuth.WebApi/swagger/index.html
Normal file
123
OpenAuth.WebApi/swagger/index.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OpenAuth.Net</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #007acc;
|
||||
}
|
||||
.header h1 {
|
||||
color: #007acc;
|
||||
margin: 0;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.header p {
|
||||
color: #666;
|
||||
margin: 10px 0 0 0;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.info-box {
|
||||
background: #e8f4fd;
|
||||
border: 1px solid #007acc;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.info-box h3 {
|
||||
color: #007acc;
|
||||
margin-top: 0;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background: #007acc;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s;
|
||||
margin: 10px 10px 10px 0;
|
||||
}
|
||||
.btn:hover {
|
||||
background: #005a9e;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: #6c757d;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #545b62;
|
||||
}
|
||||
.features {
|
||||
margin: 30px 0;
|
||||
}
|
||||
.features h3 {
|
||||
color: #007acc;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.features ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
.features li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.features li:before {
|
||||
content: "✓ ";
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
.warning {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
color: #856404;
|
||||
}
|
||||
.warning strong {
|
||||
color: #856404;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>OpenAuth.Net</h1>
|
||||
<p>最好用的.NET开源权限工作流框架</p>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<h3>⚠️重要提示</h3>
|
||||
<p>在生产环境中,SwaggerUI 默认会被停用以确保安全性。如需在生产环境显示 API 文档,请在OpenAuth.WebApi的Startup Configure中调整 </p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div style="text-align: center; margin-top: 30px; color: #666;">
|
||||
<p>© 2025 OpenAuth.Net - 开源 .NET 权限工作流框架</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,7 +21,7 @@
|
||||
<div class="flex-item" v-show="showNotFullScreen">
|
||||
<el-card shadow="nerver" class="demo-card fh">
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'"
|
||||
:table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading"
|
||||
:table-fields="firstHeaderList" :data="mainList" :loading="listLoading"
|
||||
@row-click="rowClickFirstTable"></auth-table>
|
||||
<pagination v-show="firstTotal > 0" :total="firstTotal" :page.sync="firstQuery.page"
|
||||
:limit.sync="firstQuery.limit" @pagination="handleCurrentChange" />
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</sticky>
|
||||
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'" :table-fields="headerList" :templates="['privilegeRules']" :data="list" :v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'" :table-fields="headerList" :templates="['privilegeRules']" :data="list" :loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="handleCurrentChange" />
|
||||
</el-main>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</sticky>
|
||||
<div class="app-container flex-item">
|
||||
<div class="bg-white" style="height: 100%;">
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'" :table-fields="headerList" :templates="['privilegeRules']" :data="list" :v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'" :table-fields="headerList" :templates="['privilegeRules']" :data="list" :loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="handleCurrentChange" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="app-container flex-item">
|
||||
<div class="bg-white" style="height: 100%;">
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'"
|
||||
:table-fields="headerList" :data="list" :v-loading="listLoading" @row-click="rowClick"
|
||||
:table-fields="headerList" :data="list" :loading="listLoading" @row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"></auth-table>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit"
|
||||
@pagination="handleCurrentChange" />
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<select-users v-if="postObj.verificationFinally == '1' && postObj.NodeDesignateType === 'RUNTIME_SPECIAL_USER'"
|
||||
<select-users
|
||||
v-if="postObj.verificationFinally == '1' && postObj.NodeDesignateType === 'RUNTIME_SPECIAL_USER'"
|
||||
placeholder="*选择下一个节点执行用户" :userNames.sync="postObj.NodeDesignateTxts" :users="postObj.NodeDesignates"
|
||||
:ignore-auth="true" v-on:users-change="usersChange">
|
||||
</select-users>
|
||||
<select-roles v-if="postObj.verificationFinally == '1' && postObj.NodeDesignateType === 'RUNTIME_SPECIAL_ROLE'"
|
||||
<select-roles
|
||||
v-if="postObj.verificationFinally == '1' && postObj.NodeDesignateType === 'RUNTIME_SPECIAL_ROLE'"
|
||||
placeholder="*选择下一个节点执行角色" :userNames.sync="postObj.NodeDesignateTxts" :roles="postObj.NodeDesignates"
|
||||
:ignore-auth="true" v-on:roles-change="rolesChange">
|
||||
</select-roles>
|
||||
@@ -47,6 +49,10 @@
|
||||
</v-form-render>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- url表单 -->
|
||||
<iframe v-if="flowObj.frmType === 3"
|
||||
:src="flowObj.frmData.startsWith('http') ? flowObj.frmData : ('/#' + flowObj.frmData)"
|
||||
style="width:100%;height:500px;border:none;"></iframe>
|
||||
<el-card class="box-card" v-else>
|
||||
<!--动态表单渲染-->
|
||||
<form ref="frmData">
|
||||
@@ -54,9 +60,9 @@
|
||||
<div v-else>
|
||||
<p v-html="flowObj.frmPreviewHtml"></p>
|
||||
<!-- 展示只读的数据,但需要隐藏动态表单,不然提交时没有数据 -->
|
||||
<p v-html="flowObj.frmHtml" v-show="false" ></p>
|
||||
<p v-html="flowObj.frmHtml" v-show="false"></p>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</el-card>
|
||||
<el-card class="box-card">
|
||||
@@ -231,11 +237,13 @@ export default {
|
||||
frmdata[_this.$refs.frmData[i].name] = _this.$refs.frmData[i].value
|
||||
}
|
||||
_this.postObj.frmData = JSON.stringify(frmdata)
|
||||
} else {
|
||||
} else if (_this.postObj.frmType === 2) {
|
||||
var frmdata = await _this.$refs.vFormRef.getFormData()
|
||||
_this.postObj.frmData = JSON.stringify(frmdata)
|
||||
|
||||
return
|
||||
} else {
|
||||
//URL表单暂时没有获取数据的方式
|
||||
}
|
||||
},
|
||||
usersChange(name, val) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
:select-type="'checkbox'"
|
||||
:table-fields="headerList"
|
||||
:data="list"
|
||||
:v-loading="listLoading"
|
||||
:loading="listLoading"
|
||||
@row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
></auth-table>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
:select-type="'null'"
|
||||
:table-fields="headerList"
|
||||
:data="list"
|
||||
:v-loading="listLoading"
|
||||
:loading="listLoading"
|
||||
@row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
></auth-table>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="app-container flex-item">
|
||||
<div class="bg-white" style="height: 100%;">
|
||||
<auth-table style="height:calc(100% - 60px)" ref="mainTable" :select-type="'checkbox'" :table-fields="headerList"
|
||||
:data="list" :v-loading="listLoading" @row-click="rowClick"
|
||||
:data="list" :loading="listLoading" @row-click="rowClick"
|
||||
@selection-change="handleSelectionChange"></auth-table>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit"
|
||||
@pagination="handleCurrentChange" />
|
||||
|
||||
@@ -1,81 +1,129 @@
|
||||
<template>
|
||||
<div class="flex-column">
|
||||
<div class="profile-page">
|
||||
|
||||
<div class="app-container flex-item">
|
||||
<el-row style="height: 100%;">
|
||||
<el-col :span="15" style="height: 100%;">
|
||||
<div class="bg-white" style="height: 50%;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">基本资料</el-button>
|
||||
</div>
|
||||
<el-form ref="dataForm" :model="temp" label-position="right" label-width="100px">
|
||||
<el-form-item size="small" :label="'账号'" prop="account">
|
||||
<span>{{temp.account}}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item size="small" :label="'姓名'" prop="name">
|
||||
<el-input v-model="temp.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="'性别'">
|
||||
<el-radio v-model="temp.sex" :label="0">男</el-radio>
|
||||
<el-radio v-model="temp.sex" :label="1">女</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="' '">
|
||||
<el-button size="mini" type="primary" @click="changeProfile">确认修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="bg-white" style="height: 50%;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">修改密码</el-button>
|
||||
</div>
|
||||
<el-form ref="dataForm" :model="temp" label-position="right" label-width="100px">
|
||||
<el-form-item size="small" :label="'新密码'" prop="name">
|
||||
<el-input v-model="newpwd" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="' '">
|
||||
<el-button size="mini" type="primary" @click="changePassword">确认修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4" style="height: 100%;">
|
||||
<div class="bg-white" style="height: 100%;">
|
||||
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">可访问的模块</el-button>
|
||||
<!-- 主内容区 -->
|
||||
<div class="profile-main">
|
||||
<div class="container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧用户信息区 -->
|
||||
<el-col :span="8">
|
||||
<div class="user-info-section">
|
||||
<!-- 用户头像卡片 -->
|
||||
<div class="user-avatar-card">
|
||||
<div class="avatar-container">
|
||||
<div class="user-avatar">
|
||||
<i class="el-icon-user-solid"></i>
|
||||
</div>
|
||||
<div class="user-basic-info">
|
||||
<h3 class="user-name">{{temp.name || temp.account}}</h3>
|
||||
<p class="user-account">@{{temp.account}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tree :data="modulesTree" :expand-on-click-node="false" default-expand-all :props="orgTreeProps">
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="5" style="height: 100%;border: 1px solid #EBEEF5;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">可访问的机构(✅为当前默认,点击可切换)</el-button>
|
||||
<!-- 基本信息编辑卡片 -->
|
||||
<div class="info-edit-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">基本信息</h4>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<el-form :model="temp" label-width="80px" class="user-form">
|
||||
<el-form-item label="账号">
|
||||
<div class="readonly-text">{{temp.account}}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="temp.name" placeholder="请输入姓名" size="medium"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="temp.sex">
|
||||
<el-radio :label="0">男</el-radio>
|
||||
<el-radio :label="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="changeProfile" size="medium">
|
||||
保存修改
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 密码修改卡片 -->
|
||||
<div class="password-edit-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">修改密码</h4>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<el-form label-width="80px" class="user-form">
|
||||
<el-form-item label="新密码">
|
||||
<el-input v-model="newpwd" type="password" placeholder="请输入新密码" size="medium" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="warning" @click="changePassword" size="medium">
|
||||
修改密码
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-tree :data="orgsTree" :expand-on-click-node="false" default-expand-all :props="orgTreeProps"
|
||||
@node-click="handleNodeClick"></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<!-- 右侧功能区 -->
|
||||
<el-col :span="16">
|
||||
<div class="function-section">
|
||||
<el-row :gutter="20">
|
||||
<!-- 可访问模块 -->
|
||||
<el-col :span="12">
|
||||
<div class="function-card modules-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<i class="el-icon-menu"></i>
|
||||
可访问模块
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-content tree-content">
|
||||
<el-tree
|
||||
:data="modulesTree"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:props="orgTreeProps"
|
||||
class="access-tree">
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 可访问机构 -->
|
||||
<el-col :span="12">
|
||||
<div class="function-card orgs-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<i class="el-icon-office-building"></i>
|
||||
可访问机构
|
||||
</h4>
|
||||
<span class="card-tips">✅当前默认机构,点击切换</span>
|
||||
</div>
|
||||
<div class="card-content tree-content">
|
||||
<el-tree
|
||||
:data="orgsTree"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:props="orgTreeProps"
|
||||
@node-click="handleNodeClick"
|
||||
class="access-tree clickable-tree">
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -200,22 +248,259 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
/* 页面整体样式 */
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
/* 顶部导航 */
|
||||
.profile-header {
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.el-select.filter-item.el-select--small {
|
||||
width: 100%;
|
||||
}
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.profile-main {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
/* 左侧用户信息区 */
|
||||
.user-info-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 用户头像卡片 */
|
||||
.user-avatar-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.user-avatar i {
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.user-account {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
/* 通用卡片样式 */
|
||||
.info-edit-card,
|
||||
.password-edit-card,
|
||||
.function-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.card-title i {
|
||||
margin-right: 8px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.card-tips {
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.user-form .el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-form .el-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.readonly-text {
|
||||
color: #8c8c8c;
|
||||
background: #fafafa;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-form .el-input .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.user-form .el-input .el-input__inner:focus {
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.user-form .el-radio {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.user-form .el-button {
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 右侧功能区 */
|
||||
.function-section {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.function-card {
|
||||
height: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tree-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 16px 20px 20px;
|
||||
}
|
||||
|
||||
/* 树形组件样式 */
|
||||
.access-tree {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.access-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 6px 0;
|
||||
font-size: 14px;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
.access-tree .el-tree-node__content:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.clickable-tree .el-tree-node__content {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clickable-tree .el-tree-node__content:hover {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.access-tree::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.profile-main {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="flex-item">
|
||||
<el-card shadow="nerver" class="demo-card fh">
|
||||
<auth-table style="height:calc(100% - 52px)" ref="mainTable" :select-type="'radio'"
|
||||
:table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading"
|
||||
:table-fields="firstHeaderList" :data="mainList" :loading="listLoading"
|
||||
@row-click="rowClickFirstTable"></auth-table>
|
||||
<pagination v-show="firstTotal > 0" :total="firstTotal" :page.sync="firstQuery.page"
|
||||
:limit.sync="firstQuery.limit" @pagination="handleCurrentChange" />
|
||||
|
||||
@@ -4,7 +4,7 @@ createTime: 2025/04/23 21:03:10
|
||||
permalink: /core/changesdk/
|
||||
---
|
||||
|
||||
OpenAuth.Net最新版默认使用.Net SDK 9.0.100。如果你使用的是其他版本的sdk(如.net 6.0/7.0等)打开项目,需要调整csproj项目文件的TargetFramework。用记事本等工具,打开 `Infrastructure.csproj` `OpenAuth.Repository.csproj` `OpenAuth.App.csproj` `OpenAuth.Mvc.csproj` `OpenAuth.WebApi.csproj` `OpenAuth.IdentityServer.csproj`,将
|
||||
OpenAuth.Net最新版默认使用.Net SDK 9.0.100。如果你使用的是其他版本的sdk(如.net 6.0/7.0等)打开项目,需要调整csproj项目文件的TargetFramework。用记事本等工具,打开 `Infrastructure.csproj` `OpenAuth.Repository.csproj` `OpenAuth.App.csproj` `OpenAuth.WebApi.csproj` `OpenAuth.IdentityServer.csproj`,将
|
||||
```csharp
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
@@ -8,14 +8,14 @@ permalink: /core/identity/
|
||||
## 前言
|
||||
OpenAuth.Net支持两种登录认证方式:Token认证和==自己搭建=={.tip}的OpenAuth.IdentityServer认证。
|
||||
|
||||
这两种方式通过配置webapi或mvc的appsettings.json可以自由切换:
|
||||
这两种方式通过配置webapi的appsettings.json可以自由切换:
|
||||
|
||||
```json
|
||||
"IdentityServerUrl": "http://localhost:12796", //IdentityServer服务器地址。如果为空,则不启用OAuth认证
|
||||
```
|
||||
## Token认证
|
||||
|
||||
当我们启动OpenAuth.WebApi/Mvc时,如果IdentityServerUrl为空,则采用普通的token认证,这时不需要启动OpenAuth.Identity项目:
|
||||
当我们启动OpenAuth.WebApi时,如果IdentityServerUrl为空,则采用普通的token认证,这时不需要启动OpenAuth.Identity项目:
|
||||
```json
|
||||
"IdentityServerUrl": "", //如果为空,则采用普通的token认证
|
||||
```
|
||||
@@ -32,19 +32,11 @@ OpenAuth.Net支持两种登录认证方式:Token认证和==自己搭建=={.tip
|
||||
不同于其他项目的统一登录(如微信登录、支付宝登录等),OpenAuth.Net的统一登录指的是自己搭建一套OAuth登录服务,提供给其他项目使用。即OpenAuth.IdentityServer。启动后,直接访问[http://localhost:12796](http://localhost:12796),效果如下:
|
||||

|
||||
|
||||
这时我们修改OpenAuth.WebApi/Mvc的IdentityServerUrl配置:
|
||||
这时我们修改OpenAuth.WebApi的IdentityServerUrl配置:
|
||||
```json
|
||||
"IdentityServerUrl": "http://localhost:12796", //IdentityServer服务器地址。
|
||||
```
|
||||
|
||||
### OpenAuth.Mvc效果
|
||||
|
||||
当启用了Identity时,mvc启动后界面如下:
|
||||

|
||||
|
||||
这时点击登录超链接,会跳转到OpenAuth.Identity登录界面。效果如下:
|
||||

|
||||
|
||||
|
||||
### OpenAuth.WebApi效果
|
||||
|
||||
@@ -66,7 +58,8 @@ VITE_OIDC_AUTOMATICSILENTRENEW = true #自动续期
|
||||
如果服务端启用了Identity认证,则打开登录界面如下:
|
||||

|
||||
|
||||
这时点击登录超链接,操作同OpenAuth.Mvc一样。
|
||||
这时点击登录超链接,会跳转到OpenAuth.Identity登录界面。效果如下:
|
||||

|
||||
|
||||
|
||||
#### SwaggerUI
|
||||
|
||||
@@ -3,10 +3,9 @@ title: 日志操作
|
||||
createTime: 2025/04/23 21:03:10
|
||||
permalink: /core/log/
|
||||
---
|
||||
|
||||
## 普通日志
|
||||
|
||||
框架默认使用Log4Net作为记录日志的方式,可以在Program.cs中配置日志参数或调整为其他日志。日志默认按日期生成日志文件,并存放在`log\`目录下。简单用法如下:
|
||||
框架默认使用Log4Net作为记录日志的方式,可以在Program.cs中配置日志参数或调整为其他日志。日志默认按日期生成日志文件,并存放在 `log\`目录下。简单用法如下:
|
||||
|
||||
```csharp
|
||||
//具体代码参考OpenAuth.App/OpenJobApp.cs,此处简化真实逻辑,方便理解
|
||||
@@ -25,7 +24,7 @@ permalink: /core/log/
|
||||
|
||||
## 数据库日志
|
||||
|
||||
如果想使用数据库记录业务日志(如系统默认的用户操作日志等),可以使用`SysLogApp`模块功能。日志可以在站点【消息日志】->【系统日志】中查看到记录的日志信息。简单用法如下:
|
||||
如果想使用数据库记录业务日志(如系统默认的用户操作日志等),可以使用 `SysLogApp`模块功能。日志可以在站点【消息日志】->【系统日志】中查看到记录的日志信息。简单用法如下:
|
||||
|
||||
```csharp
|
||||
//具体代码参考OpenAuth.App/OpenJobApp.cs,此处简化真实逻辑,方便理解
|
||||
@@ -49,30 +48,9 @@ permalink: /core/log/
|
||||
}
|
||||
```
|
||||
|
||||
## EF打印Sql日志
|
||||
## EF输出Sql到log4net(文件/控制台)
|
||||
|
||||
在调试数据库时,需要打印真正执行的SQL信息。最简单的方式是使用下面方法输出到控制台:
|
||||
|
||||
```csharp
|
||||
public partial class OpenAuthDBContext : DbContext
|
||||
{
|
||||
|
||||
public static readonly ILoggerFactory MyLoggerFactory
|
||||
= LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.EnableSensitiveDataLogging (true); //允许打印参数
|
||||
optionsBuilder.UseLoggerFactory (MyLoggerFactory);
|
||||
|
||||
base.OnConfiguring (optionsBuilder);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## EF输出Sql到log4net
|
||||
|
||||
框架目前直接配置`appsettings.Development.json`即可完成输出sql语句到log4net对应的日志文件中。如下:
|
||||
框架目前直接配置 `appsettings.Development.json`即可完成输出sql语句到log4net对应的日志文件中。如下:
|
||||
|
||||
```
|
||||
"Logging": {
|
||||
@@ -82,7 +60,7 @@ permalink: /core/log/
|
||||
}
|
||||
```
|
||||
|
||||
正式发布环境下,如无特殊需求,建议在`appsettings.Production.json`配置中关闭该输出
|
||||
正式发布环境下,如无特殊需求,建议在 `appsettings.Production.json`配置中关闭该输出
|
||||
|
||||
## 在Swagger中输出日志
|
||||
|
||||
@@ -90,7 +68,7 @@ permalink: /core/log/
|
||||
|
||||

|
||||
|
||||
点击`sql`列的时间,查看详细的sql执行情况
|
||||
点击 `sql`列的时间,查看详细的sql执行情况
|
||||
|
||||

|
||||
|
||||
@@ -119,6 +97,5 @@ public LoginResult Login(PassportLoginRequest request)
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
这时调用该API后即可看到具体步骤一、步骤二、步骤三的执行时间情况了
|
||||
|
||||
|
||||
|
||||
@@ -3,12 +3,17 @@ title: 多数据库
|
||||
createTime: 2025/04/23 21:03:10
|
||||
permalink: /core/multidbs/
|
||||
---
|
||||
::: tip 提示
|
||||
* 多数据库指一个应用程序同时连接和操作多个不同的数据库。
|
||||
* 多租户是架构设计模式,用户登录时选择租户信息,后面就只能访问该租户对应的数据库,即只访问一个数据库。
|
||||
|
||||
:::
|
||||
|
||||
框架支持同时访问多个数据库。具体操作如下:
|
||||
|
||||
## 添加新连接字符串
|
||||
|
||||
在配置文件appsettings.json中,添加新的连接字符串`OpenAuthDBContext2`
|
||||
在配置文件appsettings.json中,添加新的连接字符串 `OpenAuthDBContext2`
|
||||
|
||||
```csharp
|
||||
"ConnectionStrings": {
|
||||
@@ -29,7 +34,8 @@ permalink: /core/multidbs/
|
||||
|
||||
### 新建数据库访问基类
|
||||
|
||||
在项目OpenAuth.App中,新建数据库访问基类,比如`SqlSugarApp2`
|
||||
在项目OpenAuth.App中,新建数据库访问基类,比如 `SqlSugarApp2`
|
||||
|
||||
```csharp
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
@@ -60,7 +66,7 @@ namespace OpenAuth.App
|
||||
obj.Application = Assembly.GetEntryAssembly().FullName.Split(',')[0];
|
||||
Repository.Insert(obj);
|
||||
}
|
||||
|
||||
|
||||
public void Update(SysLog obj)
|
||||
{
|
||||
Repository.Update(u => new SysLog
|
||||
@@ -82,7 +88,7 @@ namespace OpenAuth.App
|
||||
|
||||
### 添加数据上下文
|
||||
|
||||
在OpenAuth.Repository中添加新的数据库上下文,比如`OpenAuthDBContext2`
|
||||
在OpenAuth.Repository中添加新的数据库上下文,比如 `OpenAuthDBContext2`
|
||||
|
||||
```csharp
|
||||
public partial class OpenAuthDBContext2 : DbContext
|
||||
@@ -119,7 +125,7 @@ namespace OpenAuth.App
|
||||
|
||||
### 注入新数据库
|
||||
|
||||
在项目OpenAuth.WebApi的启动代码`Startup.cs`中,把`ConfigureServices`方法中注入刚刚添加的数据库上下文
|
||||
在项目OpenAuth.WebApi的启动代码 `Startup.cs`中,把 `ConfigureServices`方法中注入刚刚添加的数据库上下文
|
||||
|
||||
```csharp
|
||||
services.AddDbContext<OpenAuthDBContext2>();
|
||||
|
||||
@@ -4,6 +4,12 @@ createTime: 2025/04/23 21:03:10
|
||||
permalink: /core/multitenant/
|
||||
---
|
||||
|
||||
::: tip 提示
|
||||
* 多数据库指一个应用程序同时连接和操作多个不同的数据库。
|
||||
* 多租户是架构设计模式,用户登录时选择租户信息,后面就只能访问该租户对应的数据库,即只访问一个数据库。
|
||||
|
||||
:::
|
||||
|
||||
目前市面上主流的多租户方案有三种:
|
||||
|
||||
1. **独立数据库** 即为不同的租户提供独立的数据库。
|
||||
|
||||
@@ -4,165 +4,181 @@ createTime: 2025/05/24 23:43:26
|
||||
permalink: /pro/selectusercom/
|
||||
---
|
||||
|
||||
SelectUsersCom是一个用于选择用户或角色的基础组件。如下图:
|
||||
SelectUsersCom是一个用于选择用户或角色的组件,提供了两种使用方式:
|
||||
1. 对话框模式 (index.vue)
|
||||
2. 输入框触发模式 (indexwithinput.vue)
|
||||
|
||||

|
||||

|
||||
|
||||
一般通过按钮触发弹框进行选择,如下:
|
||||
## 基础对话框组件 (index.vue)
|
||||
|
||||
```vue
|
||||
<el-button @click="selectDialog = true">选择用户</el-button>
|
||||
<el-dialog :destroy-on-close="true" width="850px" title="选择用户" v-model="selectDialog">
|
||||
<selectUsersCom v-if="selectDialog" :ignore-auth="ignoreAuth" v-model:show="selectDialog" :loginKey="'loginUser'"
|
||||
v-model:users="selectUsers" v-model:userNames="names"></selectUsersCom>
|
||||
</el-dialog>
|
||||
### 组件功能
|
||||
- 支持选择用户或角色
|
||||
- 支持组织树结构浏览
|
||||
- 支持搜索功能
|
||||
- 支持分页显示
|
||||
- 支持已选项目可视化管理
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectUsersCom from '@/components/SelectUsersCom/index.vue'
|
||||
|
||||
const selectDialog = ref(false)
|
||||
const selectUsers = ref([])
|
||||
const names = ref('')
|
||||
const ignoreAuth = ref(false)
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
还有一种通过输入框触发弹框进行选择,这时需要回填数据到文本框中。如下:
|
||||
|
||||

|
||||
|
||||
```vue
|
||||
<el-input @click="selectDialog = true" readonly v-model="names" :placeholder="placeholder"></el-input>
|
||||
<el-dialog :destroy-on-close="true" width="850px" title="选择用户" v-model="selectDialog">
|
||||
<selectUsersCom v-if="selectDialog" :ignore-auth="ignoreAuth" v-model:show="selectDialog" :loginKey="'loginUser'"
|
||||
v-model:users="selectUsers" v-model:userNames="names"></selectUsersCom>
|
||||
</el-dialog>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectUsersCom from '@/components/SelectUsersCom/index.vue'
|
||||
|
||||
const selectDialog = ref(false)
|
||||
const selectUsers = ref([])
|
||||
const names = ref('')
|
||||
const ignoreAuth = ref(false)
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
为了方便使用,我们在`SelectUsersCom`组件的基础上封装了`/components/SelectUsersCom/dialog.vue`组件用于带文本框的场景。使用如下:
|
||||
|
||||
```vue
|
||||
<select-users v-model:userNames="names" :users="users" :ignore-auth="true"></select-users>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectUsersDialog from '@/components/SelectUsersCom/dialog.vue'
|
||||
|
||||
const names = ref('')
|
||||
const roles = ref([])
|
||||
</script>
|
||||
```
|
||||
|
||||
## 属性
|
||||
### 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| show | Boolean | false | 控制组件显示/隐藏 |
|
||||
| loginKey | String | - | 选择类型,'loginUser'表示选择用户,其他值表示选择角色 |
|
||||
| orgId | String | - | 组织ID,如果为空则显示左侧树状结构 |
|
||||
| ignoreAuth | Boolean | false | 是否忽略登录用户权限,直接获取全部数据,用于可以跨部门选择用户、角色的场景 |
|
||||
| hiddenFooter | Boolean | false | 是否隐藏底部的确定/取消按钮 |
|
||||
| userNames | String | - | 已选用户/角色名称,逗号分隔 |
|
||||
| users | Array | [] | 已选用户/角色ID列表 |
|
||||
| ignoreAuth | Boolean | false | 是否忽略登录用户权限,直接获取全部数据 |
|
||||
| selectType | String | 'User' | 选择类型,'User'表示选择用户,'Role'表示选择角色 |
|
||||
| orgId | String | - | 组织ID,如果为空则显示左边树状结构 |
|
||||
| userNames | String | - | 用户名称(逗号分隔) |
|
||||
| users | Object | - | 初始选中项ID列表或对象列表 |
|
||||
| inType | String | 'id' | 父级传入的是id列表还是对象列表,取值为'id'或'obj' |
|
||||
| modelValue | Boolean | false | 控制对话框显示 |
|
||||
|
||||
## 事件
|
||||
### 事件说明
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| update:show | 组件显示状态变化时触发 | (show: Boolean) |
|
||||
| update:userNames | 选择的用户/角色名称变化时触发 | (userNames: String) |
|
||||
| update:users | 选择的用户/角色ID变化时触发 | (users: Array) |
|
||||
| 事件名 | 说明 |
|
||||
| --- | --- |
|
||||
| update:userNames | 更新用户名称 |
|
||||
| update:users | 更新用户列表 |
|
||||
| save | 保存选择结果 |
|
||||
| update:modelValue | 更新对话框显示状态 |
|
||||
|
||||
## 方法
|
||||
|
||||
| 方法名 | 说明 | 参数 |
|
||||
| --- | --- | --- |
|
||||
| handleSaveUsers | 保存已选中的用户/角色,可通过ref调用 | - |
|
||||
|
||||
## 更多示例
|
||||
|
||||
### 选择角色
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<el-input @click="selectDialog = true" readonly v-model="names" :placeholder="placeholder"></el-input>
|
||||
<el-dialog :destroy-on-close="true" width="850px" title="选择角色" v-model="selectDialog">
|
||||
<selectUsersCom v-if="selectDialog" :ignore-auth="ignoreAuth" v-model:show="selectDialog" :loginKey="'loginRole'"
|
||||
v-model:users="selectRoles" v-model:userNames="names"></selectUsersCom>
|
||||
</el-dialog>
|
||||
<template>
|
||||
<el-button @click="showDialog = true">选择用户</el-button>
|
||||
|
||||
<SelectUsersCom
|
||||
v-model="showDialog"
|
||||
:ignore-auth="true"
|
||||
select-type="User"
|
||||
v-model:users="selectedUsers"
|
||||
v-model:userNames="selectedNames"
|
||||
@save="handleSave"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectUsersCom from '@/components/SelectUsersCom/index.vue'
|
||||
|
||||
const selectDialog = ref(false)
|
||||
const selectRoles = ref([])
|
||||
const names = ref('')
|
||||
const ignoreAuth = ref(false)
|
||||
const showDialog = ref(false)
|
||||
const selectedUsers = ref([])
|
||||
const selectedNames = ref('')
|
||||
|
||||
const handleSave = () => {
|
||||
console.log('已选用户ID:', selectedUsers.value)
|
||||
console.log('已选用户名称:', selectedNames.value)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
为了方便使用,我们在`SelectUsersCom`组件的基础上封装了`SelectRoles`组件用于选择角色。使用如下:
|
||||
## 输入框触发模式 (indexwithinput.vue)
|
||||
|
||||
### 组件功能
|
||||
- 默认显示为输入框
|
||||
- 点击输入框弹出选择对话框
|
||||
- 支持根据用户ID自动获取用户姓名
|
||||
- 支持选择用户并回显名称
|
||||
|
||||
### 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| users | Array/String | - | 用户ID列表或单个用户ID |
|
||||
| userNames | String | '' | 用户名称(逗号分隔) |
|
||||
| placeholder | String | '' | 输入框占位符 |
|
||||
| ignoreAuth | Boolean | false | 是否忽略权限限制 |
|
||||
|
||||
### 事件说明
|
||||
|
||||
| 事件名 | 说明 |
|
||||
| --- | --- |
|
||||
| users-change | 用户选择变更时触发,参数为('users', 选中的用户ID列表)或('Texts', 选中的用户名称) |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<select-roles v-model:userNames="names" :roles="roles" :ignore-auth="true"></select-roles>
|
||||
<template>
|
||||
<SelectUsersComInput
|
||||
:users="selectedUserIds"
|
||||
:user-names="selectedUserNames"
|
||||
placeholder="请选择用户"
|
||||
:ignore-auth="true"
|
||||
@users-change="handleUsersChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectRoles from '@/components/SelectRoles/index.vue'
|
||||
import SelectUsersComInput from '@/components/SelectUsersCom/indexwithinput.vue'
|
||||
|
||||
const names = ref('')
|
||||
const roles = ref([])
|
||||
const selectedUserIds = ref(['user1', 'user2'])
|
||||
const selectedUserNames = ref('张三,李四')
|
||||
|
||||
const handleUsersChange = (type, value) => {
|
||||
if (type === 'users') {
|
||||
selectedUserIds.value = value
|
||||
} else if (type === 'Texts') {
|
||||
selectedUserNames.value = value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 不显示底部按钮,通过ref调用方法
|
||||
## 角色选择输入框 (SelectRoles/indexwithinput.vue)
|
||||
|
||||
### 组件功能
|
||||
- 基于SelectUsersCom实现的角色选择器
|
||||
- 默认显示为输入框
|
||||
- 点击输入框弹出选择角色对话框
|
||||
|
||||
### 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| roles | Array | - | 角色ID列表 |
|
||||
| userNames | String | - | 角色名称(逗号分隔) |
|
||||
| placeholder | String | - | 输入框占位符 |
|
||||
| ignoreAuth | Boolean | false | 是否忽略权限限制 |
|
||||
|
||||
### 事件说明
|
||||
|
||||
| 事件名 | 说明 |
|
||||
| --- | --- |
|
||||
| roles-change | 角色选择变更时触发,参数为('roles', 选中的角色ID列表)或('Texts', 选中的角色名称) |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<el-dialog width="80%" draggable :title="'选择用户'" v-model="dialogSelectUser">
|
||||
<selectUsersCom ref="selectUserRef" v-if="dialogSelectUser" :hiddenFooter="true" :loginKey="'loginUser'"
|
||||
v-model:users="selectUsers"></selectUsersCom>
|
||||
<template v-slot:footer>
|
||||
<div style="text-align: right">
|
||||
<el-button size="small" type="info" @click="dialogSelectUser = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" @click="handleSaveUsers">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<template>
|
||||
<SelectRolesInput
|
||||
:roles="selectedRoleIds"
|
||||
:user-names="selectedRoleNames"
|
||||
placeholder="请选择角色"
|
||||
:ignore-auth="true"
|
||||
@roles-change="handleRolesChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import SelectUsersCom from '@/components/SelectUsersCom/index.vue'
|
||||
import SelectRolesInput from '@/components/SelectRoles/indexwithinput.vue'
|
||||
|
||||
const selectUserRef = ref(null)
|
||||
const dialogSelectUser = ref(false)
|
||||
const selectUsers = ref([])
|
||||
const selectedRoleIds = ref(['role1', 'role2'])
|
||||
const selectedRoleNames = ref('管理员,操作员')
|
||||
|
||||
//确定添加的用户
|
||||
const handleSaveUsers = () => {
|
||||
selectUserRef.value.handleSaveUsers()
|
||||
dialogSelectUser.value = false
|
||||
const handleRolesChange = (type, value) => {
|
||||
if (type === 'roles') {
|
||||
selectedRoleIds.value = value
|
||||
} else if (type === 'Texts') {
|
||||
selectedRoleNames.value = value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 当`ignoreAuth`为`true`时,将调用`loadAll`接口获取所有数据,否则调用`getList`接口获取有权限的数据
|
||||
1. 使用indexwithinput.vue时,如果在弹出框中使用,建议设置`:destroy-on-close="true"`,避免names记录上一次选中行的值
|
||||
2. 选择用户时可以通过组织树进行筛选,也可以直接搜索
|
||||
3. ignoreAuth属性可以控制是否忽略用户权限限制,直接获取全部数据
|
||||
4. 组件内部会自动处理用户ID和用户名称的映射关系
|
||||
@@ -18,7 +18,7 @@ permalink: /pro/table/
|
||||
:table-fields="tableFields"
|
||||
:data="dataList"
|
||||
:edit-model="true"
|
||||
:v-loading="loading"
|
||||
:loading="loading"
|
||||
:templates="{'id': idtemplate}"
|
||||
select-type="checkbox"
|
||||
@row-click="handleRowClick"
|
||||
|
||||
Reference in New Issue
Block a user