47 Commits
6.2 ... v6.4

Author SHA1 Message Date
wintel
4938027aa5 fix #IADLX5 为角色分配按钮权限不起作用。 2024-07-18 21:52:02 +08:00
wintel
c8db5cb0b0 fix #IAE0NF 2024-07-18 20:50:53 +08:00
yubaolee
958a455dc4 fix #IABKQN 2024-07-11 22:21:48 +08:00
yubaolee
c5149d33c4 修复图标编辑时初始化加载 2024-07-11 21:18:07 +08:00
yubaolee
86aad6337c 修复图标初始化 2024-07-11 21:08:42 +08:00
yubaolee
099e695c27 修复按钮错位 2024-07-11 21:03:58 +08:00
yubaolee
a909d723a2 * fix IABKQN 输入框输入收清除其他下拉框或选择框的内容 2024-07-11 17:37:38 +08:00
yubaolee
2b38807d2d fix IABKQN 输入框输入收清除其他下拉框或选择框的内容 2024-07-11 16:43:21 +08:00
yubaolee
d49ed28b13 fix #I9TF4L 表单值已经发生变化,但寻找下一个节点时还是按照初始值找 2024-07-04 09:33:49 +08:00
wintel
c54fff5c34 fix #IA9CGJ 流程详情里面没有已处理 2024-06-30 18:39:39 +08:00
iafu
763bd2ff1b update doc 2024-06-24 10:14:31 +08:00
wintel
360182b297 删除WebApi生成 2024-06-23 20:57:41 +08:00
yubaolee
d923e708a4 修复流程审批字符串比较 2024-06-21 21:16:50 +08:00
yubaolee
16ba86ee71 更新文档 2024-06-19 20:49:27 +08:00
yubaolee
cdc4f60c71 修复异常 2024-06-19 20:43:42 +08:00
yubaolee
00cbe87ef5 Merge branch 'main' of https://gitee.com/dotnetchina/OpenAuth.Net 2024-06-19 20:32:29 +08:00
yubaolee
1b290ad917 更新文档 2024-06-19 20:32:23 +08:00
yubaolee
51c3eccedb Merge branch 'main' of https://gitee.com/dotnetchina/OpenAuth.Net 2024-06-19 17:38:05 +08:00
yubaolee
250658b445 fix 统计 2024-06-19 17:37:50 +08:00
李玉宝
5940280aed update README.md.
Signed-off-by: 李玉宝 <yubaolee@163.com>
2024-06-18 12:03:37 +00:00
yubaolee
161d3bfd76 update layui to 2.9.13 2024-06-18 16:28:36 +08:00
wintel
bdbab080dc update doc 2024-06-13 22:14:46 +08:00
wintel
cbbdbf2fca Merge remote-tracking branch 'origin/main' into main 2024-06-13 20:19:51 +08:00
wintel
8dfca5254d fix #I9UK5G 资源管理的查询报错 2024-06-13 20:19:40 +08:00
李玉宝
1863e833a2 update README.md.
Signed-off-by: 李玉宝 <yubaolee@163.com>
2024-06-13 02:16:39 +00:00
wintel
35ff148b68 调整查询条件类名 2024-06-12 21:22:16 +08:00
yubaolee
71a8a8e25d 增加查询对象转换为查询SQL 2024-06-12 17:39:49 +08:00
yubaolee
8ffa18dc04 routine update 2024-06-11 10:27:19 +08:00
yubaolee
cc227fa7fa fix #I9STI2 项目菜单新增和保存报错 2024-05-31 15:07:23 +08:00
yubaolee
975cd6c0a7 fix #I9T1F8 添加流程实例的时候异常 2024-05-29 11:16:03 +08:00
yubaolee
6b2e78a807 routine update 2024-05-24 09:05:10 +08:00
yubaolee
8314e88bd9 更新文档 2024-05-24 08:54:33 +08:00
yubaolee
2f968ce08c Merge branch 'main' of https://gitee.com/dotnetchina/OpenAuth.Net 2024-05-22 20:31:33 +08:00
yubaolee
43296f3504 修复文档 2024-05-22 20:31:27 +08:00
yubaolee
4979666871 增加视频教程 2024-05-21 22:07:15 +08:00
yubaolee
ac36928666 update doc 2024-05-12 17:58:30 +08:00
yubaolee
440ccd50d5 fix #I9HQWU 已审核过的用户,后面不需要再次审核 2024-04-24 10:49:29 +08:00
wintel
01a9b4e803 routine update 2024-04-22 19:12:38 +08:00
yubaolee
86d687f433 update doc 2024-04-17 14:05:58 +08:00
yubaolee
fb8f6ebb7e Merge branch 'main' of https://gitee.com/dotnetchina/OpenAuth.Net 2024-04-17 11:35:05 +08:00
yubaolee
55dccb00bc routine update 2024-04-17 11:34:59 +08:00
wintel
734bde82b2 fix #I9BQL1 2024-04-09 22:12:22 +08:00
wintel
e6ecbc633f update doc 2024-04-06 17:46:53 +08:00
wintel
ff35c5d303 fix #I9E72C 不能获取pgSql数据库结构 2024-04-05 20:15:15 +08:00
wintel
062c2b36e0 fix mvc folder 2024-04-03 21:19:54 +08:00
wintel
e07d7e0cd2 修复Oracle查询带引号 2024-03-09 19:50:00 +08:00
wintel
71b72ca06e fix #I975H7 2024-03-09 18:24:39 +08:00
36 changed files with 756 additions and 605 deletions

View File

@@ -83,9 +83,6 @@ Category="1.Database" Description="可以选择一个或多个表使用Ctrl
<%@ Register Name="ModifyReqGenerateClass"
Template="ApiGenerate\ModifyReq.cst"
MergeProperties="False" %>
<%@ Register Name="ControllerGenerateClass"
Template="ApiGenerate\Controller.cst"
MergeProperties="False" %>
开始创建OpenAuth.Core WebApi相关代码 ...
<% Generate(); %>
@@ -105,7 +102,6 @@ Category="1.Database" Description="可以选择一个或多个表使用Ctrl
}
CreateEntityClasses();
CreateControllerClass();
CreateApplicationClass();
CreateReqClass();
CreateContextClass();
@@ -154,24 +150,6 @@ Category="1.Database" Description="可以选择一个或多个表使用Ctrl
}
//创建控制器,如UserManagerController.cs
public void CreateControllerClass()
{
ControllerGenerateClass generatedClass = this.Create<ControllerGenerateClass>();
this.CopyPropertiesTo(generatedClass);
foreach(TableSchema table in tables)
{
string generatedFile = Path.GetFullPath(directory) + "OpenAuth.WebApi\\Controllers\\"+ table.Name + "sController.cs";
generatedClass.ModuleName = table.Name;
Response.WriteLine("已生成"+generatedFile);
generatedClass.RenderToFile(generatedFile, generatedFile, true);
}
}
//创建APP层,如UserManagerApp.cs
public void CreateApplicationClass()
{

View File

@@ -1,128 +0,0 @@
<%--
Name: Database Table Properties
Author: yubaolee
Description: Create a list of properties from a database table
--%>
<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="True" Description="控制器" %>
<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %>
<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>
using System;
using Infrastructure;
using Microsoft.AspNetCore.Mvc;
using OpenAuth.App;
using OpenAuth.App.Request;
using OpenAuth.App.Response;
using OpenAuth.Repository.Domain;
namespace OpenAuth.WebApi.Controllers
{
/// <summary>
/// <%=ModuleName%>操作
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class <%=ModuleName%>sController : ControllerBase
{
private readonly <%=ModuleName%>App _app;
/// <summary>
/// //获取详情
/// </summary>
[HttpGet]
public Response<<%=ModuleName%>> Get(string id)
{
var result = new Response<<%=ModuleName%>>();
try
{
result.Result = _app.Get(id);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 添加
/// </summary>
[HttpPost]
public Response Add(AddOrUpdate<%=ModuleName%>Req obj)
{
var result = new Response();
try
{
_app.Add(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 修改
/// </summary>
[HttpPost]
public Response Update(AddOrUpdate<%=ModuleName%>Req obj)
{
var result = new Response();
try
{
_app.Update(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 加载列表
/// </summary>
[HttpGet]
public TableData Load([FromQuery]Query<%=ModuleName%>ListReq request)
{
return _app.Load(request);
}
/// <summary>
/// 批量删除
/// </summary>
[HttpPost]
public Response Delete([FromBody]string[] ids)
{
var result = new Response();
try
{
_app.Delete(ids);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
public <%=ModuleName%>sController(<%=ModuleName%>App app)
{
_app = app;
}
}
}

View File

@@ -175,7 +175,7 @@ namespace Infrastructure
{
if (!string.IsNullOrEmpty(filterjson))
{
var filterGroup = JsonHelper.Instance.Deserialize<FilterGroup>(filterjson);
var filterGroup = JsonHelper.Instance.Deserialize<QueryObject>(filterjson);
query = GenerateFilter(query, parametername, filterGroup);
}
@@ -186,7 +186,7 @@ namespace Infrastructure
{
if (!string.IsNullOrEmpty(filterjson))
{
var filterGroup = JsonHelper.Instance.Deserialize<FilterGroup>(filterjson);
var filterGroup = JsonHelper.Instance.Deserialize<QueryObject>(filterjson);
query = GenerateFilter(query, parametername, filterGroup);
}
@@ -199,13 +199,13 @@ namespace Infrastructure
/// <typeparam name="T"></typeparam>
/// <param name="query"></param>
/// <param name="parametername"></param>
/// <param name="filterGroup"></param>
/// <param name="queryObject"></param>
/// <returns></returns>
public static IQueryable<T> GenerateFilter<T>(this IQueryable<T> query, string parametername,
FilterGroup filterGroup)
QueryObject queryObject)
{
var param = CreateLambdaParam<T>(parametername);
Expression result = ConvertGroup<T>(filterGroup, param);
Expression result = ConvertGroup<T>(queryObject, param);
query = query.Where(param.GenerateTypeLambda<T>(result));
return query;
}
@@ -215,14 +215,14 @@ namespace Infrastructure
/// </summary>
/// <param name="query"></param>
/// <param name="parametername"></param>
/// <param name="filterGroup"></param>
/// <param name="queryObject"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static ISugarQueryable<T> GenerateFilter<T>(this ISugarQueryable<T> query, string parametername,
FilterGroup filterGroup)
QueryObject queryObject)
{
var param = CreateLambdaParam<T>(parametername);
Expression result = ConvertGroup<T>(filterGroup, param);
Expression result = ConvertGroup<T>(queryObject, param);
query = query.Where(param.GenerateTypeLambda<T>(result));
return query;
}
@@ -230,62 +230,62 @@ namespace Infrastructure
/// <summary>
/// 转换filtergroup为表达式
/// </summary>
/// <param name="filterGroup"></param>
/// <param name="queryObject"></param>
/// <param name="param"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Expression ConvertGroup<T>(FilterGroup filterGroup, ParameterExpression param)
public static Expression ConvertGroup<T>(QueryObject queryObject, ParameterExpression param)
{
if (filterGroup == null) return null;
if (queryObject == null) return null;
if (filterGroup.Filters.Length == 1 &&(filterGroup.Children == null || !filterGroup.Children.Any())) //只有一个条件
if (queryObject.Filters.Length == 1 &&(queryObject.Children == null || !queryObject.Children.Any())) //只有一个条件
{
return param.GenerateBody<T>(filterGroup.Filters[0]);
return param.GenerateBody<T>(queryObject.Filters[0]);
}
Expression result = ConvertFilters<T>(filterGroup.Filters, param, filterGroup.Operation);
Expression gresult = ConvertGroup<T>(filterGroup.Children, param, filterGroup.Operation);
Expression result = ConvertFilters<T>(queryObject.Filters, param, queryObject.Operation);
Expression gresult = ConvertGroup<T>(queryObject.Children, param, queryObject.Operation);
if (gresult == null) return result;
if (result == null) return gresult;
if (filterGroup.Operation == "and")
if (queryObject.Operation == "and")
{
return result.AndAlso(gresult);
}
else //or
{
return result.Or(gresult);
return Expression.OrElse( result, gresult);
}
}
/// <summary>
/// 转换FilterGroup[]为表达式不管FilterGroup里面的Filters
/// </summary>
/// <param name="groups"></param>
/// <param name="queryObjs"></param>
/// <param name="param"></param>
/// <param name="operation"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
private static Expression ConvertGroup<T>(FilterGroup[] groups, ParameterExpression param, string operation)
private static Expression ConvertGroup<T>(QueryObject[] queryObjs, ParameterExpression param, string operation)
{
if (groups == null || !groups.Any()) return null;
if (queryObjs == null || !queryObjs.Any()) return null;
Expression result = ConvertGroup<T>(groups[0], param);
Expression result = ConvertGroup<T>(queryObjs[0], param);
if (groups.Length == 1) return result;
if (queryObjs.Length == 1) return result;
if (operation == "and")
{
foreach (var filter in groups.Skip(1))
foreach (var filter in queryObjs.Skip(1))
{
result = result.AndAlso(ConvertGroup<T>(filter, param));
}
}
else
{
foreach (var filter in groups.Skip(1))
foreach (var filter in queryObjs.Skip(1))
{
result = result.Or(ConvertGroup<T>(filter, param));
result = Expression.OrElse(result, ConvertGroup<T>(filter, param));
}
}
@@ -325,7 +325,7 @@ namespace Infrastructure
{
foreach (var filter in filters.Skip(1))
{
result = result.Or(param.GenerateBody<T>(filter));
result = Expression.OrElse(result, param.GenerateBody<T>(filter));
}
}

View File

@@ -1,25 +1,64 @@
namespace Infrastructure
{
public class Filter
{
public string Key { get; set; }
public string Value { get; set; }
public string Contrast { get; set; }
public string Text { get; set; }
}
public class FilterGroup
{
/// <summary>
/// or /and
/// </summary>
public string Operation { get; set; }
public Filter[] Filters { get; set; }
public FilterGroup[] Children { get; set; }
}
namespace Infrastructure
{
/// <summary>
/// 查询表达式中的最小单元,如:
/// new Filter {Key = "name", Value = "yubaolee", Contrast = "=="},
/// new Filter {Key = "name", Value = "yubaolee", Contrast = "contains"},
/// new Filter {Key = "age", Value = "10,20,30", Contrast = "in"},
/// new Filter {Key = "10,20,30", Value = "40", Contrast = "intersect"}
/// </summary>
public class Filter
{
/// <summary>
/// 过滤条件的关键字。
/// </summary>
public string Key { get; set; }
/// <summary>
/// 通常为值yubaolee、10、10,20,30等。
/// </summary>
public string Value { get; set; }
/// <summary>
/// 通常为运算符,如:==、contains、in、intersect等。
/// </summary>
public string Contrast { get; set; }
/// <summary>
/// 对于特殊值的说明
/// </summary>
public string Text { get; set; }
}
/// <summary>
/// 查询对象类,用于封装查询条件。
/// </summary>
public class QueryObject
{
/// <summary>
/// 操作类型定义了查询条件之间的逻辑关系如OR、AND。
/// </summary>
/// <remarks>
/// 该属性决定了如何组合多个过滤条件,以构建复杂的查询逻辑。
/// </remarks>
public string Operation { get; set; }
/// <summary>
/// 过滤器数组,包含一组过滤条件。
/// </summary>
public Filter[] Filters { get; set; }
/// <summary>
/// 子查询对象数组,支持嵌套查询。
/// </summary>
/// <remarks>
/// 通过嵌套查询对象,可以构建复杂的查询逻辑,处理更复杂的数据关系。
/// </remarks>
public QueryObject[] Children { get; set; }
}
}

View File

@@ -0,0 +1,51 @@
using System.Linq;
using System.Text;
namespace Infrastructure;
/// <summary>
/// 查询对象FilterGroup扩展将对象转换为sql查询
/// </summary>
public static class QueryObjExtension
{
/// <summary>
/// 根据过滤器组构建查询。
/// </summary>
public static string BuildQuery(this QueryObject query)
{
StringBuilder sb = new StringBuilder();
BuildQuery(query, sb);
return sb.ToString();
}
/// <summary>
/// 递归构建查询字符串。
/// </summary>
private static void BuildQuery(QueryObject query, StringBuilder sb)
{
// 构建当前过滤器组的过滤条件
if (query.Filters != null && query.Filters.Length > 0)
{
string filters = string.Join($" {query.Operation} ", query.Filters.Select(f => $"{f.Key} {f.Contrast} '{f.Value}'"));
sb.Append($"({filters})");
}
// 构建当前过滤器组的子过滤器组
if (query.Children != null && query.Children.Length > 0)
{
// 如果当前字符串不为空,则添加操作符连接子过滤器组
if (sb.Length > 0)
{
sb.Append($" {query.Operation} ");
}
// 使用递归方式构建子过滤器组的查询字符串,并连接到当前字符串
string children = string.Join($" {query.Operation} ", query.Children.Select(child =>
{
StringBuilder childSb = new StringBuilder();
BuildQuery(child, childSb);
return childSb.ToString();
}));
sb.Append($"({children})");
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Infrastructure.Test
[Test]
public void Convert()
{
FilterGroup sub = new FilterGroup
QueryObject sub = new QueryObject
{
Operation = "or"
};
@@ -19,22 +19,22 @@ namespace Infrastructure.Test
new Filter {Key = "c3", Value = "10,20,30", Contrast = "in"}
};
FilterGroup filterGroup = new FilterGroup
QueryObject queryObject = new QueryObject
{
Operation = "and"
};
filterGroup.Filters = new[]
queryObject.Filters = new[]
{
new Filter {Key = "c1", Value = "name", Contrast = "contains"},
new Filter {Key = "10,20,30", Value = "40", Contrast = "intersect"}
};
filterGroup.Children = new[]
queryObject.Children = new[]
{
sub
};
var expression = DynamicLinq.ConvertGroup<TestOjb>(filterGroup,
var expression = DynamicLinq.ConvertGroup<TestOjb>(queryObject,
Expression.Parameter(typeof(TestOjb), "c"));
Console.WriteLine(expression.ToString());

View File

@@ -44,8 +44,10 @@ namespace OpenAuth.App
var moduleIds = SugarClient.Queryable<Relevance>().Where(
u =>
(u.Key == Define.ROLEMODULE && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId).ToList();
var elementIds = GetElementIds();
return SugarClient.Queryable<ModuleView>().Where(m =>moduleIds.Contains(m.Id)).Includes(x=>x.Elements).ToList();
return SugarClient.Queryable<ModuleView>().Where(m =>moduleIds.Contains(m.Id))
.Includes(x=>x.Elements.Where(u=>elementIds.Contains(u.Id)).ToList()).ToList();
}
}
@@ -54,14 +56,24 @@ namespace OpenAuth.App
{
get
{
var elementIds = SugarClient.Queryable<Relevance>().Where(
u =>
(u.Key == Define.ROLEELEMENT && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId).ToList();
var elementIds = GetElementIds();
var usermoduleelements = SugarClient.Queryable<ModuleElement>().Where(u => elementIds.Contains(u.Id));
return usermoduleelements.ToList();
}
}
/// <summary>
/// 获取角色可访问的菜单Id
/// </summary>
/// <returns></returns>
private List<string> GetElementIds()
{
var elementIds = SugarClient.Queryable<Relevance>().Where(
u =>
(u.Key == Define.ROLEELEMENT && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId).ToList();
return elementIds;
}
public List<Role> Roles
{
get { return SugarClient.Queryable<Role>().Where(u => _userRoleIds.Contains(u.Id)).ToList(); }

View File

@@ -63,7 +63,7 @@ namespace OpenAuth.App
string.Join(',',orgs));
}
return UnitWork.Find<T>(null).GenerateFilter(parametername,
JsonHelper.Instance.Deserialize<FilterGroup>(rule.PrivilegeRules));
JsonHelper.Instance.Deserialize<QueryObject>(rule.PrivilegeRules));
}
/// <summary>

View File

@@ -35,7 +35,8 @@ namespace OpenAuth.App
public T Get(string id)
{
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL
|| SugarClient.CurrentConnectionConfig.DbType == DbType.Oracle)
{
return SugarClient.Queryable<T>().Where("\"Id\"=@id", new {id = id}).First();
}
@@ -74,7 +75,7 @@ namespace OpenAuth.App
string.Join(',',orgs));
}
return SugarClient.Queryable<T>().GenerateFilter(parametername,
JsonHelper.Instance.Deserialize<FilterGroup>(rule.PrivilegeRules));
JsonHelper.Instance.Deserialize<QueryObject>(rule.PrivilegeRules));
}
/// <summary>

View File

@@ -30,7 +30,13 @@ namespace OpenAuth.App
var columnFields = loginContext.GetTableColumns("Category");
if (columnFields == null || columnFields.Count == 0)
{
throw new Exception("请在代码生成界面配置Category表的字段属性");
//因为分类字典值需要在别的地方使用,必需初始化有值
columnFields = new List<BuilderTableColumn>
{
new() { ColumnName = "Name", ColumnType = "string" },
new() { ColumnName = "DtCode", ColumnType = "string" },
new() { ColumnName = "DtValue", ColumnType = "string" }
};
}
var result = new TableData();

View File

@@ -120,6 +120,10 @@ namespace OpenAuth.App
{
return GetSqlServerStructure(tableName);
}
else if (dbtype == Define.DBTYPE_PostgreSQL)
{
return GetPostgreStructure(tableName);
}
else
{
return GetOracleStructure(tableName);
@@ -258,6 +262,82 @@ order by column_id; ";
}
/// <summary>
/// 获取Mysql表结构信息
/// </summary>
private IList<SysTableColumn> GetPostgreStructure(string tableName)
{
var sql = $@"select attr.attrelid
, schema.nspname as schemaname
, class.relname as tablename --表名
, attr.attname as columnname --列名
, attr.atttypid --类型ID
, attr.atttypmod
, 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'
else 'string'
end as entitytype
, comment.description as comment
, 1 as iscolumndata
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as columnwidth
, 0 as orderno
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as maxlength
, case
when attname in ('createid', 'modifyid', '')
or con.conname is null then 0
else 1
end as isdisplay --是否显示
from pg_catalog.pg_class class
inner join pg_catalog.pg_attribute attr on attr.attrelid = class.oid
left join pg_catalog.pg_constraint con
on con.conrelid = class.oid and attr.attnum = any (con.conkey) and con.contype = 'p'
left join pg_catalog.pg_type t on attr.atttypid = t.oid
inner join pg_catalog.pg_description comment
on comment.objoid = attr.attrelid and comment.objsubid = attr.attnum
inner join pg_catalog.pg_namespace schema on schema.oid = class.relnamespace
where attr.attnum > 0
and not attr.attisdropped
and schema.nspname = 'public' -- replace 'your_schema' with your schema name
and class.relname = '{tableName}'";
foreach (var context in _contexts)
{
var columns = context.Set<SysTableColumn>().FromSqlRaw(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
return columnList;
}
}
return new List<SysTableColumn>();
}
/// <summary>
/// 获取SqlServer表结构信息
/// </summary>

View File

@@ -68,6 +68,9 @@ namespace OpenAuth.App.Flow
case DataCompare.LessEqual:
result &= frmvalue <= value;
break;
case DataCompare.NotEqual:
result &= frmvalue != value;
break;
}
}
else //如果只是字符串,只判断相等
@@ -79,7 +82,28 @@ namespace OpenAuth.App.Flow
}
else
{
result &= compare.Value == fieldVal;
switch (compare.Operation)
{
case DataCompare.Equal:
result &= compare.Value == fieldVal;
break;
case DataCompare.Larger:
result &= string.Compare(compare.Value, fieldVal, false) > 0;
break;
case DataCompare.Less:
result &= string.Compare(compare.Value, fieldVal, false) < 0;
break;
case DataCompare.LargerEqual:
result &= string.Compare(compare.Value, fieldVal, false) >= 0;
break;
case DataCompare.LessEqual:
result &= string.Compare(compare.Value, fieldVal, false) <= 0;
break;
case DataCompare.NotEqual:
result &= compare.Value != fieldVal;
break;
}
}
}

View File

@@ -26,6 +26,7 @@ using System.Linq.Expressions;
using System.Net.Http;
using System.Threading.Tasks;
using Infrastructure.Const;
using Infrastructure.Extensions;
using Infrastructure.Helpers;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
@@ -54,7 +55,8 @@ namespace OpenAuth.App
IRepository<FlowInstance, OpenAuthDBContext> repository
, RevelanceManagerApp app, FlowSchemeApp flowSchemeApp, FormApp formApp,
IHttpClientFactory httpClientFactory, IAuth auth, IServiceProvider serviceProvider,
SysMessageApp messageApp, DbExtension dbExtension, UserManagerApp userManagerApp, OrgManagerApp orgManagerApp)
SysMessageApp messageApp, DbExtension dbExtension, UserManagerApp userManagerApp,
OrgManagerApp orgManagerApp)
: base(unitWork, repository, auth)
{
_revelanceApp = app;
@@ -135,8 +137,15 @@ namespace OpenAuth.App
if (flowInstance.FrmType == 1) //如果是开发者自定义的表单
{
var t = Type.GetType("OpenAuth.App." + flowInstance.DbName + "App");
ICustomerForm icf = (ICustomerForm) _serviceProvider.GetService(t);
icf.Add(flowInstance.Id, flowInstance.FrmData);
ICustomerForm icf = (ICustomerForm)_serviceProvider.GetService(t);
try
{
icf.Add(flowInstance.Id, flowInstance.FrmData);
}
catch (Exception e)
{
throw new Exception("流程表单数据解析失败,请检查表单是否填写完整");
}
}
//如果工作流配置的表单配置有对应的数据库
@@ -247,7 +256,7 @@ namespace OpenAuth.App
if (form.FrmType == 1) //如果是开发者自定义的表单,更新对应数据库表数据
{
var t = Type.GetType("OpenAuth.App." + req.DbName + "App");
ICustomerForm icf = (ICustomerForm) _serviceProvider.GetService(t);
ICustomerForm icf = (ICustomerForm)_serviceProvider.GetService(t);
icf.Update(req.Id, req.FrmData);
}
else if (form.FrmType == 2 && !string.IsNullOrEmpty(form.DbName)) //拖拽表单定义了关联数据库
@@ -274,6 +283,7 @@ namespace OpenAuth.App
{
continue;
}
updatestr += $"{column.ColumnName} = '{val}',";
}
@@ -321,13 +331,6 @@ namespace OpenAuth.App
throw new Exception("当前用户没有审批该节点权限");
}
FlowInstanceOperationHistory flowInstanceOperationHistory = new FlowInstanceOperationHistory
{
InstanceId = instanceId,
CreateUserId = tag.UserId,
CreateUserName = tag.UserName,
CreateDate = DateTime.Now
}; //操作记录
FlowRuntime wfruntime = new FlowRuntime(flowInstance);
#region
@@ -340,7 +343,7 @@ namespace OpenAuth.App
string canCheckId = ""; //寻找当前登录用户可审核的节点Id
foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[wfruntime.currentNodeId]
.Select(u => u.to))
.Select(u => u.to))
{
var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点
canCheckId = GetOneForkLineCanCheckNodeId(fromForkStartNode, wfruntime, tag);
@@ -351,10 +354,11 @@ namespace OpenAuth.App
{
throw (new Exception("审核异常,找不到审核节点"));
}
flowInstanceOperationHistory.Content =
var content =
$"{user.Account}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}审批了【{wfruntime.Nodes[canCheckId].name}】" +
$"结果:{(tag.Taged == 1 ? "" : "")},备注:{tag.Description}";
AddOperationHis(instanceId, tag, content);
wfruntime.MakeTagNode(canCheckId, tag); //标记审核节点状态
string res = wfruntime.NodeConfluence(canCheckId, tag);
@@ -382,78 +386,33 @@ namespace OpenAuth.App
flowInstance.MakerList = GetForkNodeMakers(wfruntime, wfruntime.currentNodeId);
AddTransHistory(wfruntime);
}
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj());
}
#endregion
#region
else
{
wfruntime.MakeTagNode(wfruntime.currentNodeId, tag);
if (tag.Taged == (int) TagState.Ok)
{
bool canNext = true;
if (wfruntime.currentNode.setInfo.NodeDesignate == Setinfo.RUNTIME_MANY_PARENTS)
{
var roles = _auth.GetCurrentUser().Roles;
//如果是连续多级直属上级且还没到指定的角色,只改变执行人,不到下一个节点
if (!wfruntime.currentNode.setInfo.NodeDesignateData.roles.Intersect(roles.Select(u =>u.Id)).Any())
{
canNext = false;
var parentId = _userManagerApp.GetParent(user.Id);
flowInstance.MakerList = parentId;
}
}
if (canNext)
{
flowInstance.PreviousId = flowInstance.ActivityId;
flowInstance.ActivityId = wfruntime.nextNodeId;
flowInstance.ActivityType = wfruntime.nextNodeType;
flowInstance.ActivityName = wfruntime.nextNode.name;
flowInstance.MakerList = wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime, request);
flowInstance.IsFinish = (wfruntime.nextNodeType == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
}
}
else
{
flowInstance.IsFinish = FlowInstanceStatus.Disagree; //表示该节点不同意
wfruntime.nextNodeId = "-1";
wfruntime.nextNodeType = 4;
}
AddTransHistory(wfruntime);
flowInstanceOperationHistory.Content =
$"{user.Account}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}审批了【{wfruntime.currentNode.name}】" +
$"结果:{(tag.Taged == 1 ? "" : "")},备注:{tag.Description}";
VerifyNode(request, tag, flowInstance);
}
#endregion
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj());
if (!string.IsNullOrEmpty(request.FrmData))
//自定义开发表单,需要更新对应的数据库
if (!string.IsNullOrEmpty(request.FrmData) && flowInstance.FrmType == 1)
{
flowInstance.FrmData = request.FrmData;
if (flowInstance.FrmType == 1) //如果是开发者自定义的表单,更新对应数据库表数据
{
var t = Type.GetType("OpenAuth.App." + flowInstance.DbName + "App");
ICustomerForm icf = (ICustomerForm) _serviceProvider.GetService(t);
icf.Update(flowInstance.Id, flowInstance.FrmData);
}
var t = Type.GetType("OpenAuth.App." + flowInstance.DbName + "App");
ICustomerForm icf = (ICustomerForm)_serviceProvider.GetService(t);
icf.Update(flowInstance.Id, flowInstance.FrmData);
}
UnitWork.Update(flowInstance);
UnitWork.Add(flowInstanceOperationHistory);
//给流程创建人发送通知信息
_messageApp.SendMsgTo(flowInstance.CreateUserId,
$"你的流程[{flowInstance.CustomName}]已被{user.Name}处理。处理情况如下:{flowInstanceOperationHistory.Content}");
$"你的流程[{flowInstance.CustomName}]已被{user.Name}处理。");
UnitWork.Save();
@@ -461,6 +420,72 @@ namespace OpenAuth.App
return true;
}
/// <summary>
/// 普通的节点审批
/// </summary>
private void VerifyNode(VerificationReq request, Tag tag, FlowInstance flowInstance)
{
var user = _auth.GetCurrentUser().User;
if (!string.IsNullOrEmpty(request.FrmData))
{
flowInstance.FrmData = request.FrmData;
}
FlowRuntime wfruntime = new FlowRuntime(flowInstance);
wfruntime.MakeTagNode(wfruntime.currentNodeId, tag);
if (tag.Taged == (int)TagState.Ok)
{
bool canNext = true;
if (wfruntime.currentNode.setInfo.NodeDesignate == Setinfo.RUNTIME_MANY_PARENTS)
{
var roles = _auth.GetCurrentUser().Roles;
//如果是连续多级直属上级且还没到指定的角色,只改变执行人,不到下一个节点
if (!wfruntime.currentNode.setInfo.NodeDesignateData.roles.Intersect(roles.Select(u => u.Id)).Any())
{
canNext = false;
var parentId = _userManagerApp.GetParent(user.Id);
flowInstance.MakerList = parentId;
}
}
if (canNext)
{
flowInstance.PreviousId = flowInstance.ActivityId;
flowInstance.ActivityId = wfruntime.nextNodeId;
flowInstance.ActivityType = wfruntime.nextNodeType;
flowInstance.ActivityName = wfruntime.nextNode.name;
flowInstance.MakerList = wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime, request);
flowInstance.IsFinish = (wfruntime.nextNodeType == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
}
}
else //审批结果为不同意
{
flowInstance.IsFinish = FlowInstanceStatus.Disagree;
wfruntime.nextNodeId = "-1";
wfruntime.nextNodeType = 4;
}
var content =
$"{user.Account}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}审批了【{wfruntime.currentNode.name}】" +
$"结果:{(tag.Taged == 1 ? "" : "")},备注:{tag.Description}";
AddOperationHis(flowInstance.Id, tag, content);
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj());
//如果审批通过,且下一个审批人是自己,则自动审批
if (tag.Taged == (int)TagState.Ok)
{
if (flowInstance.MakerList != "1" && (!flowInstance.MakerList.Contains(user.Id)))
{
return;
}
VerifyNode(request, tag, flowInstance);
}
}
//会签时,获取一条会签分支上面是否有用户可审核的节点
private string GetOneForkLineCanCheckNodeId(FlowNode fromForkStartNode, FlowRuntime wfruntime, Tag tag)
{
@@ -507,7 +532,7 @@ namespace OpenAuth.App
var tag = new Tag
{
Description = reqest.VerificationOpinion,
Taged = (int) TagState.Reject,
Taged = (int)TagState.Reject,
UserId = user.Id,
UserName = user.Name
};
@@ -593,7 +618,7 @@ namespace OpenAuth.App
makerList = GenericHelpers.ArrayToString(request.NodeDesignates, makerList);
}
else if (wfruntime.nextNode.setInfo.NodeDesignate == Setinfo.RUNTIME_PARENT
else if (wfruntime.nextNode.setInfo.NodeDesignate == Setinfo.RUNTIME_PARENT
|| wfruntime.nextNode.setInfo.NodeDesignate == Setinfo.RUNTIME_MANY_PARENTS)
{
//如果是上一节点执行人的直属上级或连续多级直属上级
@@ -606,8 +631,12 @@ namespace OpenAuth.App
//当审批流程时,能进到这里,表明当前登录用户已经有审批当前节点的权限,完全可以直接用登录用户的直接上级
var user = _auth.GetCurrentUser().User;
var parentId = _userManagerApp.GetParent(user.Id);
makerList = GenericHelpers.ArrayToString(new[]{parentId}, makerList);
if (StringExtension.IsNullOrEmpty(parentId))
{
throw new Exception("无法找到当前用户的直属上级");
}
makerList = GenericHelpers.ArrayToString(new[] { parentId }, makerList);
}
else if (wfruntime.nextNode.setInfo.NodeDesignate == Setinfo.RUNTIME_CHAIRMAN)
{
@@ -664,7 +693,7 @@ namespace OpenAuth.App
{
if (node.setInfo != null && node.setInfo.Taged != null)
{
if (node.type != FlowNode.FORK && node.setInfo.Taged != (int) TagState.Ok) //如果节点是不同意或驳回,则不用再找了
if (node.type != FlowNode.FORK && node.setInfo.Taged != (int)TagState.Ok) //如果节点是不同意或驳回,则不用再找了
{
break;
}
@@ -710,7 +739,8 @@ namespace OpenAuth.App
}
else if (node.setInfo != null)
{
if (string.IsNullOrEmpty(node.setInfo.NodeDesignate) ||node.setInfo.NodeDesignate == Setinfo.ALL_USER) //所有成员
if (string.IsNullOrEmpty(node.setInfo.NodeDesignate) ||
node.setInfo.NodeDesignate == Setinfo.ALL_USER) //所有成员
{
makerList = "1";
}
@@ -751,7 +781,7 @@ namespace OpenAuth.App
CheckNodeDesignate(request);
}
bool isReject = TagState.Reject.Equals((TagState) Int32.Parse(request.VerificationFinally));
bool isReject = TagState.Reject.Equals((TagState)Int32.Parse(request.VerificationFinally));
if (isReject) //驳回
{
NodeReject(request);
@@ -815,7 +845,7 @@ namespace OpenAuth.App
// 加入搜索自定义标题
if (!string.IsNullOrEmpty(request.key))
{
waitExp = waitExp.And(t => t.CustomName.Contains(request.key));
waitExp = PredicateBuilder.And(waitExp, t => t.CustomName.Contains(request.key));
}
result.count = await UnitWork.Find(waitExp).CountAsync();
@@ -849,7 +879,7 @@ namespace OpenAuth.App
// 加入搜索自定义标题
if (!string.IsNullOrEmpty(request.key))
{
myFlowExp = myFlowExp.And(t => t.CustomName.Contains(request.key));
myFlowExp = PredicateBuilder.And(myFlowExp, t => t.CustomName.Contains(request.key));
}
result.count = await UnitWork.Find(myFlowExp).CountAsync();
@@ -882,6 +912,20 @@ namespace OpenAuth.App
});
}
private void AddOperationHis(string instanceId, Tag tag, string content)
{
FlowInstanceOperationHistory flowInstanceOperationHistory = new FlowInstanceOperationHistory
{
InstanceId = instanceId,
CreateUserId = tag.UserId,
CreateUserName = tag.UserName,
CreateDate = DateTime.Now,
Content = content
}; //操作记录
UnitWork.Add(flowInstanceOperationHistory);
}
public List<FlowInstanceOperationHistory> QueryHistories(QueryFlowInstanceHistoryReq request)
{
return UnitWork.Find<FlowInstanceOperationHistory>(u => u.InstanceId == request.FlowInstanceId)

View File

@@ -85,7 +85,8 @@ namespace OpenAuth.App
}
var columnnames = columnFields.Select(u => u.ColumnName);
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL
|| SugarClient.CurrentConnectionConfig.DbType == DbType.Oracle)
{
columnnames = columnFields.Select(u => "\"" + u.ColumnName +"\"");
}

View File

@@ -41,7 +41,8 @@ namespace OpenAuth.App
}
var columnnames = columnFields.Select(u => u.ColumnName);
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL
|| SugarClient.CurrentConnectionConfig.DbType == DbType.Oracle)
{
columnnames = columnFields.Select(u => "\"" + u.ColumnName +"\"");
}

View File

@@ -18,7 +18,7 @@ namespace OpenAuth.App.Test
var services = new ServiceCollection();
var cachemock = new Mock<ICacheContext>();
cachemock.Setup(x => x.Get<UserAuthSession>("tokentest")).Returns(new UserAuthSession { Account = "Systems" });
cachemock.Setup(x => x.Get<UserAuthSession>("tokentest")).Returns(new UserAuthSession { Account = "admin" });
services.AddScoped(x => cachemock.Object);
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
@@ -37,7 +37,7 @@ namespace OpenAuth.App.Test
{
var app = _autofacServiceProvider.GetService<ResourceApp>();
var result = app.Load(new QueryResourcesReq());
Console.WriteLine(JsonHelper.Instance.Serialize(result));
Console.WriteLine(JsonHelper.Instance.Serialize(result.Result));
}
[Test]
@@ -46,7 +46,7 @@ namespace OpenAuth.App.Test
var auth = _autofacServiceProvider.GetService<IAuth>();
var app = _autofacServiceProvider.GetService<DataPrivilegeRuleApp>();
//该测试解析为:针对资源列表,【管理员】可以看到所有,角色为【神】或【测试】的只能看到自己创建的
var filterGroup = new FilterGroup
var filterGroup = new QueryObject
{
Operation = "or"
};
@@ -61,7 +61,7 @@ namespace OpenAuth.App.Test
};
filterGroup.Children = new[]
{
new FilterGroup //登录用户角色包含【测试】或包含【神】的,只能看到自己的
new QueryObject //登录用户角色包含【测试】或包含【神】的,只能看到自己的
{
Operation = "and",
Filters = new Filter[]

View File

@@ -62,7 +62,7 @@
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?0558502420ce5fee054b31425e77ffa6";
hm.src = "//hm.baidu.com/hm.js?93a7b9a145222f9b7109d643a0c58f8d";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();

View File

@@ -446,28 +446,6 @@
<None Include="wwwroot\layui\images\face\8.gif" />
<None Include="wwwroot\layui\images\face\9.gif" />
<None Include="wwwroot\layui\layui.js" />
<None Include="wwwroot\layui\lay\dest\layui.all.js" />
<None Include="wwwroot\layui\lay\modules\carousel.js" />
<None Include="wwwroot\layui\lay\modules\code.js" />
<None Include="wwwroot\layui\lay\modules\colorpicker.js" />
<None Include="wwwroot\layui\lay\modules\element.js" />
<None Include="wwwroot\layui\lay\modules\flow.js" />
<None Include="wwwroot\layui\lay\modules\form.js" />
<None Include="wwwroot\layui\lay\modules\jquery.js" />
<None Include="wwwroot\layui\lay\modules\laydate.js" />
<None Include="wwwroot\layui\lay\modules\layedit.js" />
<None Include="wwwroot\layui\lay\modules\layer.js" />
<None Include="wwwroot\layui\lay\modules\layim.js" />
<None Include="wwwroot\layui\lay\modules\laypage.js" />
<None Include="wwwroot\layui\lay\modules\laytpl.js" />
<None Include="wwwroot\layui\lay\modules\mobile.js" />
<None Include="wwwroot\layui\lay\modules\rate.js" />
<None Include="wwwroot\layui\lay\modules\slider.js" />
<None Include="wwwroot\layui\lay\modules\table.js" />
<None Include="wwwroot\layui\lay\modules\transfer.js" />
<None Include="wwwroot\layui\lay\modules\tree.js" />
<None Include="wwwroot\layui\lay\modules\upload.js" />
<None Include="wwwroot\layui\lay\modules\util.js" />
<None Include="wwwroot\userJs\assignModule.js" />
<None Include="wwwroot\userJs\assignResource.js" />
<None Include="wwwroot\userJs\assignRole.js" />

View File

@@ -1,9 +1,10 @@
@section header
{
<link rel="stylesheet" href="/css/treetable.css" />
<link rel="stylesheet" href="/js/dtree/dtree.css" />
<link rel="stylesheet" href="/js/dtree/font/dtreefont.css" />
<link rel="stylesheet" href="/css/treetable.css"/>
<link rel="stylesheet" href="/js/dtree/dtree.css"/>
<link rel="stylesheet" href="/js/dtree/font/dtreefont.css"/>
}
<blockquote class="layui-elem-quote news_search toolList" id="menus">
</blockquote>
@@ -18,46 +19,45 @@
lay-data="{height: 'full-80', id:'mainList'}"
lay-filter="list" lay-size="sm">
<thead>
<tr>
<th lay-data="{checkbox:true, fixed: true}"></th>
<th lay-data="{field:'Name', width:120, sort: true, fixed: true}">模块名称</th>
<th lay-data="{field:'Status', width:100,templet: '#statusTpl'}">导航栏展示</th>
<th lay-data="{field:'IsSys', width:100,templet: '#sysTpl'}">模块类型</th>
<th lay-data="{field:'IconName', width:60,templet: '#iconTpl'}">图标</th>
<th lay-data="{field:'CascadeId', width:80}">层级ID</th>
<th lay-data="{field:'Code', width:80}">模块标识</th>
<th lay-data="{field:'Url', width:200}">Url</th>
<th lay-data="{field:'ParentName', width:135}">父节点名称</th>
<th lay-data="{field:'SortNo', width:80}">排序号</th>
</tr>
<tr>
<th lay-data="{checkbox:true, fixed: true}"></th>
<th lay-data="{field:'Name', width:120, sort: true, fixed: true}">模块名称</th>
<th lay-data="{field:'Status', width:100,templet: '#statusTpl'}">导航栏展示</th>
<th lay-data="{field:'IsSys', width:100,templet: '#sysTpl'}">模块类型</th>
<th lay-data="{field:'IconName', width:60,templet: '#iconTpl'}">图标</th>
<th lay-data="{field:'CascadeId', width:80}">层级ID</th>
<th lay-data="{field:'Code', width:80}">模块标识</th>
<th lay-data="{field:'Url', width:200}">Url</th>
<th lay-data="{field:'ParentName', width:135}">父节点名称</th>
<th lay-data="{field:'SortNo', width:80}">排序号</th>
</tr>
</thead>
</table>
<script type="text/html" id="iconTpl">
{{# if( d.IconName != null && d.IconName != ''){ }}
{{# if( d.IconName != null && d.IconName != ''){ }}
<i class="layui-icon {{ d.IconName }}"></i>
{{# } else { }}
{{# } else { }}
{{ d.IconName }}
{{# } }}
{{# } }}
</script>
<script type="text/html" id="statusTpl">
{{# if( d.Status == -1){ }}
{{# if( d.Status == -1){ }}
<span style="color:red">隐藏</span>
{{# } else { }}
{{# } else { }}
<span style="color:green">显示</span>
{{# } }}
{{# } }}
</script>
<script type="text/html" id="sysTpl">
{{# if( d.IsSys == 1){ }}
<script type="text/html" id="sysTpl">
{{# if( d.IsSys == 1){ }}
<span class="layui-badge">内置</span>
{{# } else { }}
{{# } else { }}
<span class="layui-badge layui-bg-green">自定义</span>
{{# } }}
{{# } }}
</script>
</div>
<div class="layui-col-xs">
<div class="layui-col-xs3">
<!--菜单列表-->
<table class="layui-table"
lay-data="{height: 'full-80', id:'menuList', text: { none: '单击模块列表数据行查看菜单' }}"
@@ -69,32 +69,31 @@
</tr>
</thead>
</table>
<script type="text/html" id="menuTpl">
<button class="layui-btn layui-btn-xs {{ d.Class }} layui-btn-fluid" alt="{{ d.DomId }}"> <i class="layui-icon {{ d.Icon }}"></i> {{ d.Name }}</button>
</script>
</div>
</div>
<!--模块添加/编辑窗口-->
<div id="divEdit" style="display: none">
<form class="layui-form layui-form-pane" action="" id="formEdit">
<form class="layui-form layui-form-pane" action="" id="formEdit" lay-filter="formEdit">
<input type="hidden" name="Id" v-model="tmp.Id" />
<input type="hidden" name="Id"/>
<div class="layui-form-item">
<label class="layui-form-label">模块名称</label>
<div class="layui-input-block">
<input type="text" name="Name" v-model="tmp.Name" required lay-verify="required"
<input type="text" name="Name" required lay-verify="required"
placeholder="请输入模块名称" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">导航栏展示</label>
<div class="layui-input-block">
<select name="Status" v-model="tmp.Status" required lay-verify="required">
<option value="0" selected="selected" >正常</option>
<select name="Status" required lay-verify="required">
<option value="0" selected="selected">正常</option>
<option value="-1">隐藏</option>
</select>
@@ -103,27 +102,27 @@
<div class="layui-form-item">
<label class="layui-form-label">模块标识</label>
<div class="layui-input-block">
<input type="text" name="Code" v-model="tmp.Code"
<input type="text" name="Code"
placeholder="比如Module" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">URL地址</label>
<div class="layui-input-block">
<input type="text" name="Url" v-model="tmp.Url" required lay-verify="required"
<input type="text" name="Url" required lay-verify="required"
placeholder="请输入URL" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">图标</label>
<div class="layui-input-block">
<input type="text" id="IconName" name="IconName" v-model="tmp.IconName" lay-filter="iconPicker" class="layui-input">
<input type="text" id="IconName" name="IconName" lay-filter="iconPicker" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序号</label>
<div class="layui-input-block">
<input type="text" name="SortNo" v-model="tmp.SortNo" required lay-verify="required"
<input type="text" name="SortNo" required lay-verify="required"
placeholder="请输入排序号" autocomplete="off" class="layui-input">
</div>
</div>
@@ -131,9 +130,8 @@
<div class="layui-form-item">
<label class="layui-form-label">所属模块</label>
<div class="layui-input-block">
<input id="ParentName" name="ParentName" v-model="tmp.ParentName" class="layui-input" />
<input id="ParentId" name="ParentId" v-model="tmp.ParentId" type="hidden" />
<input id="ParentName" name="ParentName" class="layui-input"/>
<input id="ParentId" name="ParentId" type="hidden"/>
</div>
</div>
@@ -148,15 +146,15 @@
<!--添加菜单窗口-->
<div id="divMenuEdit" style="display: none">
<form class="layui-form layui-form-pane" action="" id="mfromEdit">
<form class="layui-form layui-form-pane" action="" id="mfromEdit" lay-filter="mfromEdit">
<input type="hidden" name="Id" v-model="tmp.Id" />
<input type="hidden" name="ModuleId" v-model="tmp.ModuleId" />
<input type="hidden" name="Id"/>
<input type="hidden" name="ModuleId"/>
<div class="layui-form-item">
<label class="layui-form-label">菜单名称</label>
<div class="layui-input-block">
<input type="text" name="Name" v-model="tmp.Name" required lay-verify="required"
<input type="text" name="Name" required lay-verify="required"
placeholder="请输入名称" autocomplete="off" class="layui-input">
</div>
</div>
@@ -164,28 +162,28 @@
<div class="layui-form-item">
<label class="layui-form-label">DomId</label>
<div class="layui-input-block">
<input type="text" name="DomId" v-model="tmp.DomId" required lay-verify="required"
<input type="text" name="DomId" required lay-verify="required"
placeholder="请输入DomId" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-form-item">
<label class="layui-form-label">图标</label>
<div class="layui-input-block">
<input type="text" id="Icon" name="Icon" v-model="tmp.Icon" lay-filter="btnIconPicker" class="layui-input">
<input type="text" id="Icon" name="Icon" lay-filter="btnIconPicker" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">样式</label>
<div class="layui-input-block">
<input type="text" name="Class" v-model="tmp.Class"
<input type="text" name="Class"
placeholder="菜单的样式layui-btn-danger" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序号</label>
<div class="layui-input-block">
<input type="text" name="Sort" v-model="tmp.Sort" required lay-verify="required"
<input type="text" name="Sort" required lay-verify="required"
placeholder="请输入排序号" autocomplete="off" class="layui-input">
</div>
</div>
@@ -200,4 +198,4 @@
</div>
<script type="text/javascript" src="/layui/layui.js"></script>
<script type="text/javascript" src="/userJs/modules.js"></script>
<script type="text/javascript" src="/userJs/modules.js?v2"></script>

View File

@@ -56,21 +56,21 @@
<!--用户添加/编辑窗口-->
<div id="divEdit" style="display: none">
<form class="layui-form layui-form-pane" action="" id="formEdit">
<form class="layui-form layui-form-pane" action="" id="formEdit" lay-filter="formEdit">
<input type="hidden" name="Id" v-model="tmp.Id" />
<input type="hidden" name="Id" />
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-block">
<input type="text" name="Account" v-model="tmp.Account" required lay-verify="required"
<input type="text" name="Account" required lay-verify="required"
placeholder="请输入登录账号" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">姓名</label>
<div class="layui-input-block">
<input type="text" name="Name" v-model="tmp.Name" required lay-verify="required"
<input type="text" name="Name" required lay-verify="required"
placeholder="请输入昵称或姓名" autocomplete="off" class="layui-input">
</div>
</div>
@@ -78,8 +78,8 @@
<div class="layui-form-item">
<label class="layui-form-label">所属部门</label>
<div class="layui-input-block">
<input id="Organizations" name="Organizations" v-model="tmp.Organizations" required lay-verify="required" class="layui-input" />
<input id="OrganizationIds" name="OrganizationIds" v-model="tmp.OrganizationIds" required lay-verify="required" type="hidden" />
<input id="Organizations" name="Organizations" required lay-verify="required" class="layui-input" />
<input id="OrganizationIds" name="OrganizationIds" required lay-verify="required" type="hidden" />
</div>
</div>
@@ -87,14 +87,14 @@
<div class="layui-form-item" pane>
<label class="layui-form-label">性别</label>
<div class="layui-input-block">
<input type="radio" name="Sex" value="1" title="男" v-model="tmp.Sex" >
<input type="radio" name="Sex" value="0" title="女" v-model="tmp.Sex" >
<input type="radio" name="Sex" value="1" title="男" >
<input type="radio" name="Sex" value="0" title="女" >
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label">禁用</label>
<div class="layui-input-block">
<input type="checkbox" name="Status" v-model="tmp.Status" lay-skin="switch" value="1">
<input type="checkbox" name="Status" lay-skin="switch" value="1">
</div>
</div>
<div class="layui-form-item">
@@ -107,4 +107,4 @@
</div>
<script type="text/javascript" src="/layui/layui.js"></script>
<script type="text/javascript" src="/userJs/users.js?v2.0"></script>
<script type="text/javascript" src="/userJs/users.js?v3.0"></script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -44,7 +44,7 @@ layui.config({
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?0558502420ce5fee054b31425e77ffa6";
hm.src = "https://hm.baidu.com/hm.js?93a7b9a145222f9b7109d643a0c58f8d";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();

View File

@@ -1,62 +1,39 @@
layui.config({
base: "/js/"
}).use(['form','vue', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'iconPicker', 'utils'], function () {
}).use(['form', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'iconPicker', 'utils'], function () {
var form = layui.form,
layer = layui.layer,
$ = layui.jquery;
var iconPicker = layui.iconPicker;
var btnIconPicker = layui.iconPicker;
var vmMenu = new Vue({
el: "#mfromEdit",
data(){
return {
tmp: {}
}
},
watch:{
tmp(val){
this.$nextTick(function () {
form.render(); //刷新select等
btnIconPicker.checkIcon('btnIconPicker', this.tmp.Icon);
})
}
},
mounted(){
form.render();
}
});
var vmModule = new Vue({
el: "#formEdit",
data(){
return {
tmp: {} //使用一个tmp封装一下后面可以直接用vm.tmp赋值
}
},
watch:{
tmp(val){
this.$nextTick(function () {
form.render(); //刷新select等
layui.droptree("/UserSession/GetModules", "#ParentName", "#ParentId", false);
iconPicker.checkIcon('iconPicker', this.tmp.IconName);
})
}
},
mounted(){
form.render();
layui.droptree("/UserSession/GetModules", "#ParentName", "#ParentId", false);
}
});
var moduleInitVal = { //模块初始化的值
Id: "",
SortNo: 1,
IconName: 'layui-icon-app',
ParentId: '',
ParentName: '',
Name: '',
Url: '',
Status: 0,
Remark: '',
Code: ''
};
var menuInital={ //菜单初始化的值
Id: "",
ModuleId: "",
Name:'',
DomeId:'',
Class:'',
Sort: 1,
Icon: 'layui-icon-app'
}
iconPicker.render({
// 选择器推荐使用input
elem: '#IconName',
type: 'fontClass',
// 每个图标格子的宽度:'43px'或'20%'
cellWidth: '43px',
// 点击回调
click: function (data) {
vmModule.tmp.IconName = data.icon;
}
});
btnIconPicker.render({ //按钮的图标
// 选择器推荐使用input
@@ -64,19 +41,15 @@ layui.config({
type: 'fontClass',
// 每个图标格子的宽度:'43px'或'20%'
cellWidth: '43px',
// 点击回调
click: function (data) {
vmMenu.tmp.Icon = data.icon;
}
});
var table = layui.table;
var openauth = layui.openauth;
$("#menus").loadMenus("Module");
//主列表加载,可反复调用进行刷新
var config= {}; //table的参数如搜索key点击tree的id
var config = {}; //table的参数如搜索key点击tree的id
var mainList = function (options) {
if (options != undefined) {
$.extend(config, options);
@@ -86,7 +59,7 @@ layui.config({
where: config
, response: {
statusCode: 200 //规定成功的状态码默认0
}
}
});
}
@@ -101,7 +74,7 @@ layui.config({
where: menucon
, response: {
statusCode: 200 //规定成功的状态码默认0
}
}
});
}
@@ -110,7 +83,7 @@ layui.config({
var url = '/UserSession/GetModules';
var zTreeObj;
var setting = {
view: { selectedMulti: false },
view: {selectedMulti: false},
data: {
key: {
name: 'Name',
@@ -125,17 +98,17 @@ layui.config({
},
callback: {
onClick: function (event, treeId, treeNode) {
mainList({ pId: treeNode.Id });
mainList({pId: treeNode.Id});
}
}
};
var load = function () {
$.getJSON(url, function (json) {
zTreeObj = $.fn.zTree.init($("#tree"), setting);
var newNode = { Name: "根节点", Id: null, ParentId: "" };
var newNode = {Name: "根节点", Id: null, ParentId: ""};
json.Result.push(newNode);
zTreeObj.addNodes(null, json.Result);
mainList({ pId: "" });
mainList({pId: ""});
zTreeObj.expandAll(true);
});
};
@@ -146,35 +119,35 @@ layui.config({
}();
$("#tree").height($("div.layui-table-view").height());
//添加(编辑)模块对话框
var editDlg = function() {
var update = false; //是否为更新
var show = function (data) {
var editDlg = function () {
var show = function (update, data) {
var title = update ? "编辑信息" : "添加";
layer.open({
title: title,
area: ["500px", "480px"],
type: 1,
content: $('#divEdit'),
success: function() {
if(data.Id ==''){
for(var key in vmModule.tmp){
delete vmModule.tmp[key];
}
success: function () {
layui.droptree("/UserSession/GetModules", "#ParentName", "#ParentId", false);
if (data == undefined) {
form.val("formEdit", moduleInitVal);
} else {
form.val("formEdit", data);
iconPicker.checkIcon('iconPicker', data.IconName);
}
vmModule.tmp = Object.assign({}, vmModule.tmp,data)
},
end: mainList
});
var url = "/moduleManager/Add";
if (update) {
url = "/moduleManager/Update";
url = "/moduleManager/Update";
}
//提交数据
form.on('submit(formSubmit)',
function(data) {
function (data) {
$.post(url,
data.field,
function(data) {
function (data) {
layer.msg(data.Message);
if ((!update) && data.Code == 200) { //添加成功要刷新左边的树
ztree.reload();
@@ -185,25 +158,18 @@ layui.config({
});
}
return {
add: function() { //弹出添加
update = false;
show({
Id: "",
SortNo: 1,
IconName:'layui-icon-app'
});
add: function () { //弹出添加
show(false);
},
update: function(data) { //弹出编辑框
update = true;
show(data);
update: function (data) { //弹出编辑框
show(true,data);
}
};
}();
//添加菜单对话框
var meditDlg = function () {
var update = false; //是否为更新
var show = function (data) {
var show = function (update, data) {
var title = update ? "编辑信息" : "添加";
layer.open({
title: title,
@@ -211,12 +177,12 @@ layui.config({
type: 1,
content: $('#divMenuEdit'),
success: function () {
if(data.Id ==''){
for(var key in vmMenu.tmp){
delete vmMenu.tmp[key];
}
if (data == undefined) {
form.val("mfromEdit", menuInital);
} else {
form.val("mfromEdit", data);
btnIconPicker.checkIcon('btnIconPicker', data.Icon);
}
vmMenu.tmp = Object.assign({}, vmMenu.tmp,data)
},
end: menuList
});
@@ -238,24 +204,17 @@ layui.config({
}
return {
add: function (moduleId) { //弹出添加
update = false;
show({
Id: "",
ModuleId:moduleId,
Sort: 1,
Icon:'layui-icon-app'
});
show(false);
},
update: function (data) { //弹出编辑框
update = true;
show(data);
show(true,data);
}
};
}();
//监听行单击事件
table.on('row(list)', function(obj){
menuList({moduleId:obj.data.Id});
table.on('row(list)', function (obj) {
menuList({moduleId: obj.data.Id});
});
//监听页面主按钮操作
@@ -264,7 +223,9 @@ layui.config({
var checkStatus = table.checkStatus('mainList')
, data = checkStatus.data;
openauth.del("/moduleManager/Delete",
data.map(function (e) { return e.Id; }),
data.map(function (e) {
return e.Id;
}),
function () {
mainList();
ztree.reload();
@@ -274,7 +235,9 @@ layui.config({
var checkStatus = table.checkStatus('menuList')
, data = checkStatus.data;
openauth.del("/moduleManager/DelMenu",
data.map(function (e) { return e.Id; }),
data.map(function (e) {
return e.Id;
}),
menuList);
}
, btnAdd: function () { //添加模块
@@ -289,15 +252,15 @@ layui.config({
}
meditDlg.add(data[0].Id);
}
, btnEdit: function () { //编辑
var checkStatus = table.checkStatus('mainList')
, data = checkStatus.data;
if (data.length != 1) {
layer.msg("请选择编辑的行,且同时只能编辑一行");
return;
}
editDlg.update(data[0]);
}
, btnEdit: function () { //编辑
var checkStatus = table.checkStatus('mainList')
, data = checkStatus.data;
if (data.length != 1) {
layer.msg("请选择编辑的行,且同时只能编辑一行");
return;
}
editDlg.update(data[0]);
}
, btnEditMenu: function () { //编辑菜单
var checkStatus = table.checkStatus('menuList')
@@ -310,9 +273,9 @@ layui.config({
}
, search: function () { //搜索
mainList({ key: $('#key').val() });
mainList({key: $('#key').val()});
}
, btnRefresh: function() {
, btnRefresh: function () {
mainList();
}
};

View File

@@ -7,6 +7,16 @@ layui.config({
var table = layui.table;
var openauth = layui.openauth;
var toplayer = (top == undefined || top.layer === undefined) ? layer : top.layer; //顶层的LAYER
var initVal = { //初始化数据
Id: '',
Account:'',
Name:'',
Organizations:'',
OrganizationIds:'',
Sex:'',
Status:0
};
$("#menus").loadMenus("User");
@@ -68,9 +78,7 @@ layui.config({
//添加(编辑)对话框
var editDlg = function() {
var vm;
var update = false; //是否为更新
var show = function (data) {
var show = function (update, data) {
var title = update ? "编辑信息" : "添加";
layer.open({
title: title,
@@ -78,34 +86,18 @@ layui.config({
type: 1,
content: $('#divEdit'),
success: function() {
if(vm == undefined){
vm = new Vue({
el: "#formEdit",
data(){
return {
tmp:data //使用一个tmp封装一下后面可以直接用vm.tmp赋值
}
},
watch:{
tmp(val){
this.$nextTick(function () {
form.render(); //刷新select等
//layui.droptree("/UserSession/GetOrgs", "#Organizations", "#OrganizationIds");
})
}
},
mounted(){
form.render();
var _this = this;
layui.droptree("/UserSession/GetOrgs", "#Organizations", "#OrganizationIds", true,function (ids, names) {
_this.tmp.OrganizationIds = ids;
_this.tmp.Organizations = names;
});
}
if (data == undefined) {
form.val("formEdit", initVal);
} else {
layui.droptree("/UserSession/GetOrgs", "#Organizations", "#OrganizationIds", true,function (ids, names) {
form.val("formEdit", {
Organizations: names,
OrganizationIds: ids
});
});
}else{
vm.tmp = Object.assign({}, vm.tmp,data)
}
form.val("formEdit", data);
}
},
end: mainList
});
@@ -127,22 +119,15 @@ layui.config({
//重置
$("#reset").click(function(){
vm.tmp = {
OrganizationIds:'',
Organizations:''
}
});
}
return {
add: function() { //弹出添加
update = false;
show({
Id: ''
});
show(false);
},
update: function(data) { //弹出编辑框
update = true;
show(data);
show(true,data);
}
};
}();

View File

@@ -94,7 +94,7 @@ namespace OpenAuth.Repository.Domain
/// 前端界面是否缓存
/// </summary>
/// [Description("前端界面是否缓存")]
public bool KeepAlive { get; set; }
public bool? KeepAlive { get; set; }
}
}

View File

@@ -1,32 +1,29 @@
using System;
using System.Linq;
using Infrastructure;
using Infrastructure.Extensions;
using Infrastructure.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenAuth.Repository.Domain;
using OpenAuth.Repository.QueryObj;
namespace OpenAuth.Repository
{
public partial class OpenAuthDBContext : DbContext
{
private ILoggerFactory _LoggerFactory;
private IHttpContextAccessor _httpContextAccessor;
private IConfiguration _configuration;
private IOptions<AppSetting> _appConfiguration;
public OpenAuthDBContext(DbContextOptions<OpenAuthDBContext> options, ILoggerFactory loggerFactory,
IHttpContextAccessor httpContextAccessor, IConfiguration configuration, IOptions<AppSetting> appConfiguration)
IHttpContextAccessor httpContextAccessor, IConfiguration configuration,
IOptions<AppSetting> appConfiguration)
: base(options)
{
_LoggerFactory = loggerFactory;
@@ -37,7 +34,7 @@ namespace OpenAuth.Repository
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging(true); //允许打印参数
optionsBuilder.EnableSensitiveDataLogging(true); //允许打印参数
optionsBuilder.UseLoggerFactory(_LoggerFactory);
InitTenant(optionsBuilder);
base.OnConfiguring(optionsBuilder);
@@ -46,10 +43,9 @@ namespace OpenAuth.Repository
//初始化多租户信息根据租户id调整数据库
private void InitTenant(DbContextOptionsBuilder optionsBuilder)
{
var tenantId = _httpContextAccessor.GetTenantId();
string connect = _configuration.GetConnectionString(tenantId);
if(string.IsNullOrEmpty(connect))
if (string.IsNullOrEmpty(connect))
{
throw new Exception($"未能找到租户{tenantId}对应的连接字符串信息");
}
@@ -59,15 +55,15 @@ namespace OpenAuth.Repository
.ToDictionary(x => x.Key, x => x.Value);
var dbType = dbtypes[tenantId];
if(dbType == Define.DBTYPE_SQLSERVER)
if (dbType == Define.DBTYPE_SQLSERVER)
{
optionsBuilder.UseSqlServer(connect);
}
else if(dbType == Define.DBTYPE_MYSQL) //mysql
else if (dbType == Define.DBTYPE_MYSQL) //mysql
{
optionsBuilder.UseMySql(connect, new MySqlServerVersion(new Version(8, 0, 11)));
}
else if(dbType == Define.DBTYPE_PostgreSQL) //PostgreSQL
else if (dbType == Define.DBTYPE_PostgreSQL) //PostgreSQL
{
optionsBuilder.UseNpgsql(connect);
}
@@ -75,15 +71,33 @@ namespace OpenAuth.Repository
{
optionsBuilder.UseOracle(connect, options => options.UseOracleSQLCompatibility("11"));
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DataPrivilegeRule>()
.HasKey(c => new { c.Id });
.HasKey(c => new {c.Id});
modelBuilder.Entity<SysTableColumn>().HasNoKey();
modelBuilder.Entity<QueryStringObj>().HasNoKey();
//converting between PostgreSQL smallint and .NET Boolean types
if (Database.ProviderName == "Npgsql.EntityFrameworkCore.PostgreSQL"
|| Database.ProviderName == "Oracle.EntityFrameworkCore")
{
var boolToSmallIntConverter = new ValueConverter<bool, short>(
v => v ? (short) 1 : (short) 0,
v => v != 0);
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType == typeof(bool))
{
property.SetValueConverter(boolToSmallIntConverter);
}
}
}
}
}
public virtual DbSet<Application> Applications { get; set; }
@@ -112,13 +126,11 @@ namespace OpenAuth.Repository
public virtual DbSet<WmsInboundOrderTbl> WmsInboundOrderTbls { get; set; }
public virtual DbSet<OpenJob> OpenJobs { get; set; }
public virtual DbSet<BuilderTable> BuilderTables { get; set; }
public virtual DbSet<BuilderTableColumn> BuilderTableColumns { get; set; }
//非数据库表格
public virtual DbSet<QueryStringObj> QueryStringObjs { get; set; }
public virtual DbSet<SysTableColumn> SysTableColumns { get; set; }
}
}
}
}

View File

@@ -59,7 +59,7 @@ namespace OpenAuth.Repository.Test
[Test]
public void TestDynamic()
{
FilterGroup sub = new FilterGroup
QueryObject sub = new QueryObject
{
Operation = "or"
};
@@ -69,24 +69,24 @@ namespace OpenAuth.Repository.Test
new Filter {Key = "Sex", Value = "10", Contrast = "=="}
};
FilterGroup filterGroup = new FilterGroup
QueryObject queryObject = new QueryObject
{
Operation = "and"
};
filterGroup.Filters = new[]
queryObject.Filters = new[]
{
new Filter {Key = "Account", Value = "name", Contrast = "=="},
new Filter {Key = "Password", Value = "10", Contrast = "=="}
};
filterGroup.Children = new[]
queryObject.Children = new[]
{
sub
};
var dbcontext = _autofacServiceProvider.GetService<OpenAuthDBContext>();
var query = dbcontext.Users.GenerateFilter("c",JsonHelper.Instance.Serialize(filterGroup));
var query = dbcontext.Users.GenerateFilter("c",JsonHelper.Instance.Serialize(queryObject));
Console.WriteLine(query.Expression.ToString());
}
}

View File

@@ -73,7 +73,7 @@ namespace OpenAuth.Repository.Test
[Test]
public void TestDynamic()
{
FilterGroup sub = new FilterGroup
QueryObject sub = new QueryObject
{
Operation = "or"
};
@@ -83,24 +83,24 @@ namespace OpenAuth.Repository.Test
new Filter {Key = "Sex", Value = "10", Contrast = "=="}
};
FilterGroup filterGroup = new FilterGroup
QueryObject queryObject = new QueryObject
{
Operation = "and"
};
filterGroup.Filters = new[]
queryObject.Filters = new[]
{
new Filter {Key = "Account", Value = "name", Contrast = "=="},
new Filter {Key = "Password", Value = "10", Contrast = "=="}
};
filterGroup.Children = new[]
queryObject.Children = new[]
{
sub
};
var sugarClient = _autofacServiceProvider.GetService<ISqlSugarClient>();
var query = sugarClient.Queryable<User>().GenerateFilter("c",filterGroup);
var query = sugarClient.Queryable<User>().GenerateFilter("c",queryObject);
Console.WriteLine(query.ToSqlString());
}
}

View File

@@ -1,18 +1,5 @@
🔥.Net权限管理及快速开发框架、最好用的权限工作流系统。源于Martin Fowler企业级应用开发思想及最新技术组合SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等。核心模块包括角色授权、代码生成、智能打印、表单设计、工作流、定时任务等。架构易扩展是中小企业的首选。
## ❤❤❤郑重声明❤❤❤
主分支main运行环境默认为.Net SDK 6.0如果你使用vs2019作为开发工具请注意查看[VS2019打开6.0及以后版本](http://doc.openauth.net.cn/core/faq.html#vs2019%E6%89%93%E5%BC%806-0%E5%8F%8A%E4%BB%A5%E5%90%8E%E7%89%88%E6%9C%AC)
需要.Net SDK 4.0/4.5开发环境的同学请查看本项目4.0分支,已停止维护
使用.Net Core 2.1--3.1的请看:
**GitHub** https://github.com/yubaolee/OpenAuth.Core
**码云** https://gitee.com/yubaolee/OpenAuth.Core
![LOGO](https://gitee.com/uploads/images/2018/0425/163228_7077c3fd_362401.png "1.png")
**logo图标含义** OpenAuth中OA字母的结合体整体像鱼授人以渔你非说像咸鱼那也是积极向上的咸鱼中心是个笑脸微笑面对生活(✿◡‿◡)。
@@ -21,7 +8,6 @@
**官方文档** http://doc.openauth.net.cn
![](https://img.shields.io/badge/release-6.0-blue)
![](https://img.shields.io/badge/SqlSugar-5.1.4-blue)
![](https://img.shields.io/badge/IdentityServer4-3.0.1-blue)
@@ -41,6 +27,23 @@
![](https://img.shields.io/badge/npm-9.7.1-brightgreen)
![](https://img.shields.io/badge/layui-2.8.6-brightgreen)
## ❤❤❤郑重声明❤❤❤
主分支main运行环境默认为.Net SDK 6.0,支持.NET未来版本需要.Net SDK 4.0/4.5开发环境的同学请查看本项目4.0分支,已停止维护
使用.Net Core 2.1-3.1的请进https://gitee.com/yubaolee/OpenAuth.Core ,已停止维护
## OpenAuth.Net系列视频火热更新中
[OpenAuth.Net视频合集--系统结构及代码下载](https://www.bilibili.com/video/BV1Z1421q7xU/)
[OpenAuth.Net视频合集--基于RBAC体系的权限管理介绍](https://www.bilibili.com/video/BV1M9KeejENf/)
[OpenAuth.Net视频合集--企业版代码启动](https://www.bilibili.com/video/BV1KSuQebEek/)
[OpenAuth.Net视频合集--使用企业版代码生成器](https://www.bilibili.com/video/BV1JCuyeaEFp/)
[OpenAuth.Net视频合集--表单设计](https://www.bilibili.com/video/BV1dagEeFEVA/)
## 关于OpenAuth.Net企业版/高级版的说明:

View File

@@ -1,11 +1,12 @@
/*
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Date: 2023-08-12 10:48:24
* @LastEditTime: 2023-12-30 21:10:18
* @LastEditTime: 2024-06-19 20:48:12
* @Description:
* @
* @Copyright (c) 2023 by yubaolee | ahfu~ , All Rights Reserved.
*/
module.exports = {
title: 'OpenAuth.Net',
description: '最好用的.net权限工作流框架,最好用的.net vue前后分离框架',
@@ -13,7 +14,15 @@ module.exports = {
['link', {
rel: 'icon',
href: '/logo.png'
}]
}],['script',{},
`var _hmt = _hmt || [];
(function () {
var hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?93a7b9a145222f9b7109d643a0c58f8d'
var s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})();
`]
],
extendMarkdown: (md) => {
md.set({

View File

@@ -0,0 +1,28 @@
/*
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Date: 2024-06-19 20:41:42
* @LastEditTime: 2024-06-19 20:48:04
* @Description:
* @
* @Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
*/
// .vuepress/enhanceApp.js
export default ({ router }) => {
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined') {
// 百度统计代码
var _hmt = _hmt || []
;(function () {
var hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?93a7b9a145222f9b7109d643a0c58f8d'
var s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})();
// 路由切换时触发百度统计
router.afterEach(function (to) {
if (window._hmt) {
window._hmt.push(['_trackPageview', to.fullPath])
}
})
}
}

View File

@@ -39,11 +39,18 @@ CodeSmith Generator Studio 8.0或以上
如下图使用CodeSmith文件夹中的模板右击【ApiGenerate.cst】--【Execute】选择需要生成的表本文以Stock为例及相关的上下文命名空间点击【Generate】
![](http://pj.openauth.net.cn/zentao/file-read-26.jpg)
![20240613220037](http://img.openauth.net.cn/20240613220037.png)
注意,有两个配置项:
* WholeDb: 如果选中,则按数据库中所有表生成实体及逻辑;否则,按选择的表生成
* HeaderModel会生成主、从表结构类似 WmsInboundOrderTbl / WmsInboundOrderDtbl
生成成功后在CodeSmith/Csharp文件夹下面会有Stock实体相关文档如下图
![](http://pj.openauth.net.cn/zentao/file-read-53.png)
![20240613220224](http://img.openauth.net.cn/20240613220224.png)
把CSharp\OpenAuth.App覆盖到自己项目对应目录
@@ -51,7 +58,6 @@ CodeSmith Generator Studio 8.0或以上
**把CSharp\OpenAuth.Repository\OpenAuthDBContext.cs中的内容添加到自己项目的文件中千万不要直接覆盖文件**
**其他文件夹的内容为WebAPI项目使用可以不管。**
## 添加界面
@@ -69,7 +75,15 @@ userJs直接覆盖到OpenAuth.Mvc/wwwroot中
## 添加模块
编写完上面代码后运行系统使用System账号登录系统在【模块管理】中添加`仓储管理`模块,并为它添加菜单这里我只添加一个菜单【btnAdd】如下图
编写完上面代码后运行系统使用System账号登录系统在【模块管理】中添加`仓储管理`模块,
![20240613220434](http://img.openauth.net.cn/20240613220434.png)
::: warning 注意
因为生成的Controller名称类似XXXsController所以模块的Url地址应该是XXXs/Index
:::
并为它添加菜单这里我只添加一个菜单【btnAdd】如下图
![](http://pj.openauth.net.cn/zentao/file-read-51.png)

View File

@@ -1,3 +1,29 @@
# 概念
## 会签
会签又称为联名签署,指需要得到两个或多个相关参与者的签名批准。目前支持两种模式:全部通过和至少一个通过,在【会签开始】节点进行配置。如下:
![20240417112438](http://img.openauth.net.cn/20240417112438.png)
全部通过:会签中的所有人员都通过,节点审批通过。
至少一个通过:会签中任何一个人通过,节点即审批通过。
具体的会签人员或角色需要在【会签开始】和【会签结束】之间的连线配置如上图中的admin、test。
::: warning 特别注意
【会签开始】【会签结束】执行权限配置为所有人
会签不能在分支上加判断条件
:::
## 条件分支
有时需要根据提交数据不同(如报销金额、请假天数等)流程转向不同的审批者。这时需要在连线上面配置分支条件,如下图:
![20240417114340](http://img.openauth.net.cn/20240417114340.png)
# 基本操作
一个完整的工作流包括流程设计及流程实例处理。分别对应系统中【基础配置/流程设计】及【流程中心】两个板块。具体包含以下几个步骤:

View File

@@ -1,6 +1,6 @@
OpenAuth.Pro是一套全新的前端界面基于vue-element-admin采用VUE全家桶VUE+VUEX+VUE-ROUTER单页面SPA开发。它使用开源版OpenAuth.Net的API接口OpenAuth.WebApi提供数据服务。二者的关系如下
![](/architect.png)
![系统架构](http://img.openauth.net.cn/系统架构.png)
企业版代码获取方式:[http://www.openauth.net.cn/question/detail.html?id=a2be2d61-7fcb-4df8-8be2-9f296c22a89c](http://www.openauth.net.cn/question/detail.html?id=a2be2d61-7fcb-4df8-8be2-9f296c22a89c)
@@ -8,6 +8,16 @@ OpenAuth.Pro是一套全新的前端界面基于vue-element-admin采用VUE
企业H5版本演示请使用浏览器移动模式或直接用手机打开[http://demo.openauth.net.cn:1804/](http://demo.openauth.net.cn:1804/)
## OpenAuth.Net系列教学视频
[OpenAuth.Net视频合集--系统结构及代码下载](https://www.bilibili.com/video/BV1Z1421q7xU/)
[OpenAuth.Net视频合集--企业版代码启动](https://www.bilibili.com/video/BV1KSuQebEek/)
[OpenAuth.Net视频合集--使用企业版代码生成器](https://www.bilibili.com/video/BV1JCuyeaEFp/)
[OpenAuth.Net视频合集--权限管理介绍](https://www.bilibili.com/video/BV1M9KeejENf/)
## 工具准备
@@ -60,19 +70,24 @@ OpenAuth.Pro v4.3及以前的版本使用Node 14
:::
使用npm run dev 命令运行。如下图
![20211214232229](http://img.openauth.net.cn/20211214232229.png)
启动成功后,使用浏览器访问[http://localhost:1803/](http://localhost:1803/) 即可打开企业版界面
::: warning 注意事项
开发环境配置文件为`.env.dev`,调试前请调整为自己的接口地址:
修改配置文件`.env.dev`对应的后端接口地址,调整为自己的接口地址
```javascript
VUE_APP_BASE_API = http://localhost:52789/api
VUE_APP_BASE_IMG_URL = http://localhost:52789
```
::: warning 注意事项
如果是发布打包,调整的文件为`.env.prod`
:::
使用npm run dev 命令运行。如下图:
![20211214232229](http://img.openauth.net.cn/20211214232229.png)
启动成功后,使用浏览器访问[http://localhost:1803/](http://localhost:1803/) 即可打开企业版界面

View File

@@ -1,5 +1,7 @@
# 企业版代码生成器
本章节视频讲解请参考:[OpenAuth.Net视频合集--使用企业版代码生成器](https://www.bilibili.com/video/BV1JCuyeaEFp/)
## 术语解释
在添加新功能之前需要先了解OpenAuth.Net生成代码时的两个概念动态头部和固定头部
@@ -49,17 +51,21 @@ initCfg() {
-- mysql示例
create table stock
(
createtime datetime not null comment '操作时间',
status int not null comment '出库/入库',
price decimal(10, 1) not null comment '产品单价',
number int not null comment '产品数量',
name text not null comment '产品名称',
orgid varchar(50) null comment '组织ID',
user varchar(50) not null comment '操作人',
viewable varchar(50) not null comment '可见范围',
id varchar(50) not null comment '数据ID'
primary key
Id varchar(50) not null comment '数据ID'
primary key,
Name text not null comment '产品名称',
Number int not null comment '产品数量',
Price decimal(10, 1) not null comment '产品单价',
Status int not null comment '出库/入库',
Viewable varchar(50) not null comment '可见范围',
User varchar(50) not null comment '操作人',
Time datetime not null comment '操作时间',
OrgId varchar(50) null comment '组织ID'
)
comment '出入库信息表' charset = utf8
row_format = COMPACT;
```
@@ -80,6 +86,7 @@ create table stock
这时,编辑字段具体的属性。如是否【可显示】【可编辑】等。
## 子表添加
::: warning 注意
@@ -135,6 +142,10 @@ src\views\stocks\index.vue
![20211208011431](http://img.openauth.net.cn/20211208011431.png)
## 其他
当采用动态头部时,如果数据库新加了字段,需要使用【同步结构】功能把新字段同步到代码生成器中。为了防止对已有的配置造成影响,该功能只新增字段,不会对已有字段进行调整。