Prechádzať zdrojové kódy

添加项目文件。

CrazyIter 4 rokov pred
rodič
commit
46ffd4f2f9
69 zmenil súbory, kde vykonal 4806 pridanie a 0 odobranie
  1. 25 0
      CMS.sln
  2. 24 0
      CMS/CMS.csproj
  3. 35 0
      CMS/Context/BaseConfigModel.cs
  4. 54 0
      CMS/Context/BizException.cs
  5. 174 0
      CMS/Context/HttpGlobalExceptionInvoke.cs
  6. 11 0
      CMS/Context/IBusinessService.cs
  7. 424 0
      CMS/Context/SourceDBContext.cs
  8. 425 0
      CMS/Context/TargetDBContext.cs
  9. 25 0
      CMS/Controllers/EtlController.cs
  10. 27 0
      CMS/Extension/Captcha/HeiCaptchaExtension.cs
  11. 30 0
      CMS/Extension/Captcha/ImageRgba32Extension.cs
  12. 217 0
      CMS/Extension/Captcha/ImageSharpExtension.cs
  13. 282 0
      CMS/Extension/Captcha/SecurityCodeHelper.cs
  14. 16 0
      CMS/Extension/JsonRpcRequest/BaseJosnRPCRequest.cs
  15. 13 0
      CMS/Extension/JsonRpcRequest/JosnRPCRequest.cs
  16. 15 0
      CMS/Extension/JsonRpcRequest/PaginationJosnRPCRequest.cs
  17. 14 0
      CMS/Extension/JsonRpcResponse/BaseJosnRPCResponse.cs
  18. 16 0
      CMS/Extension/JsonRpcResponse/DataJosnRPCResponse.cs
  19. 16 0
      CMS/Extension/JsonRpcResponse/ErrorJosnRPCResponse.cs
  20. 13 0
      CMS/Extension/JsonRpcResponse/ErrorModel.cs
  21. 13 0
      CMS/Extension/JsonRpcResponse/JosnRPCResponse.cs
  22. 156 0
      CMS/Extension/JsonRpcResponse/JsonRPCResponseBuilder.cs
  23. 16 0
      CMS/Extension/JsonRpcResponse/JsonRPCResult.cs
  24. 17 0
      CMS/Extension/JsonRpcResponse/PageJosnRPCResponse.cs
  25. 15 0
      CMS/Extension/JsonRpcResponse/PageJsonRPCResult.cs
  26. 40 0
      CMS/Extension/Jwt/BlackListJwtSecurityTokenHandler.cs
  27. 33 0
      CMS/Extension/Jwt/ClaimModel.cs
  28. 12 0
      CMS/Extension/Jwt/HttpConstant.cs
  29. 103 0
      CMS/Extension/Jwt/JwtAuth.cs
  30. 11 0
      CMS/Extension/Jwt/JwtBlackRecord.cs
  31. 13 0
      CMS/Extension/Jwt/JwtClient.cs
  32. 97 0
      CMS/Extension/Jwt/JwtHelper.cs
  33. 15 0
      CMS/Extension/Jwt/JwtResponse.cs
  34. 31 0
      CMS/Extension/Jwt/JwtSetting.cs
  35. 15 0
      CMS/Extension/Jwt/JwtTokenOptions.cs
  36. 212 0
      CMS/Extension/Jwt/RSAUtils.cs
  37. 25 0
      CMS/Extension/Jwt/RsaHelper.cs
  38. 31 0
      CMS/Extension/PageToken/Pagination.cs
  39. 12 0
      CMS/Extension/PageToken/PaginationData.cs
  40. 12 0
      CMS/Extension/RequestData/BaseRequest.cs
  41. 10 0
      CMS/Extension/RequestData/PaginationRequest.cs
  42. 43 0
      CMS/Helpers/CollectionHelper/CollectionHelper.cs
  43. 43 0
      CMS/Helpers/CollectionHelper/ObjectToDictionaryHelper.cs
  44. 36 0
      CMS/Helpers/Constant.cs
  45. 25 0
      CMS/Helpers/HttpClientHelper.cs
  46. 38 0
      CMS/Helpers/JsonHelper/ClassSerializers.cs
  47. 29 0
      CMS/Helpers/JsonHelper/JsonApiHelper.cs
  48. 206 0
      CMS/Helpers/JsonHelper/JsonNetHelper.cs
  49. 18 0
      CMS/Helpers/JsonHelper/JsonPath/IJsonPathValueSystem.cs
  50. 105 0
      CMS/Helpers/JsonHelper/JsonPath/JsonApiValueSystem.cs
  51. 64 0
      CMS/Helpers/JsonHelper/JsonPath/JsonNetValueSystem.cs
  52. 480 0
      CMS/Helpers/JsonHelper/JsonPath/JsonPathContext.cs
  53. 140 0
      CMS/Helpers/PredicateExtensions.cs
  54. 173 0
      CMS/Helpers/RSACrypt/RsaHelper.cs
  55. 137 0
      CMS/Helpers/ShaHash/ShaHashHelper.cs
  56. 94 0
      CMS/Helpers/ToolGoodUtils.cs
  57. 51 0
      CMS/Models/Source/SourceExercise.cs
  58. 50 0
      CMS/Models/Target/TargetExercise.cs
  59. 32 0
      CMS/Program.cs
  60. 30 0
      CMS/Properties/launchSettings.json
  61. 56 0
      CMS/Services/DataETLService.cs
  62. 14 0
      CMS/Services/SourceService.cs
  63. 13 0
      CMS/Services/TargetService.cs
  64. 71 0
      CMS/Startup.cs
  65. 32 0
      CMS/appsettings.Development.json
  66. 10 0
      CMS/appsettings.json
  67. 35 0
      CMS/log4net.config
  68. 27 0
      CMS/private.pem
  69. 9 0
      CMS/public.pem

+ 25 - 0
CMS.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30128.74
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CMS", "CMS\CMS.csproj", "{C10D2D18-225A-4BCA-B2FB-87EA62F58AB0}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{C10D2D18-225A-4BCA-B2FB-87EA62F58AB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C10D2D18-225A-4BCA-B2FB-87EA62F58AB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C10D2D18-225A-4BCA-B2FB-87EA62F58AB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C10D2D18-225A-4BCA-B2FB-87EA62F58AB0}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {A175119C-5CE2-4AB7-ABCE-0DA39166B449}
+	EndGlobalSection
+EndGlobal

+ 24 - 0
CMS/CMS.csproj

@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="CSRedisCore" Version="3.6.5" />
+    <PackageReference Include="Hei.Captcha" Version="0.2.0" />
+    <PackageReference Include="IdentityModel" Version="4.3.0" />
+    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.5" />
+    <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.6.0" />
+    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.2" />
+    <PackageReference Include="BCrypt.Net-Core" Version="1.6.0" />
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
+    <PackageReference Include="MySql.Data" Version="8.0.20" />
+    <PackageReference Include="Serilog" Version="2.9.0" />
+    <PackageReference Include="sqlSugarCore" Version="5.0.0.10" />
+    <PackageReference Include="System.Drawing.Common" Version="4.7.0" />
+    <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.6.0" />
+    <PackageReference Include="ToolGood.Words" Version="3.0.1.2" />
+    <PackageReference Include="PdfSharpCore" Version="1.1.26" />
+    <PackageReference Include="Scrutor" Version="3.2.0" />
+  </ItemGroup>
+</Project>

+ 35 - 0
CMS/Context/BaseConfigModel.cs

@@ -0,0 +1,35 @@
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Context
+{
+    public class BaseConfigModel
+    {
+        public static IConfiguration Configuration { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+
+        public static string ContentRootPath { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+
+        public static string WebRootPath { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="config"></param>
+        /// <param name="contentRootPath"></param>
+        /// <param name="webRootPath"></param>
+        public static void SetBaseConfig(IConfiguration config, string contentRootPath, string webRootPath)
+        {
+            Configuration = config;
+            ContentRootPath = contentRootPath;
+            WebRootPath = webRootPath;
+        }
+    }
+}

+ 54 - 0
CMS/Context/BizException.cs

@@ -0,0 +1,54 @@
+using System;
+
+namespace TEAMModelOS.SDK.Context.Exception
+{
+    public class BizException : System.Exception
+    {
+        public string message { get; set; } = "error";
+        public int code { get; set; } = 1;
+        public string devMessage { get; set; }
+        public BizException(String message,  int code,  string stackTrace) : base(message)
+        {
+            if (string.IsNullOrEmpty(stackTrace))
+            {
+                this.devMessage = this.StackTrace;
+            }
+            else
+            {
+                this.devMessage = stackTrace;
+            }
+            this.message = message;
+            this.code = code;
+        }
+        public BizException( String message,  int code) : base(message)
+        {
+            this.devMessage = this.StackTrace;
+            this.message = message;
+            this.code = code;
+        }
+        //
+        // 摘要:
+        //     Initializes a new instance of the System.Exception class.
+        public BizException() : base()
+        {
+            this.devMessage = this.StackTrace;
+            this.message = "error";
+            this.code = 1;
+        }
+
+        //
+        // 摘要:
+        //     Initializes a new instance of the System.Exception class with a specified error
+        //     message.
+        //
+        // 参数:
+        //   message:
+        //     The message that describes the error.
+        public BizException(string message) : base(message)
+        {
+            this.message = message;
+            this.devMessage = this.StackTrace;
+            this.code = 1;
+        }
+    }
+}

+ 174 - 0
CMS/Context/HttpGlobalExceptionInvoke.cs

@@ -0,0 +1,174 @@
+using HiTeachCE.Helpers;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Helper.Common.JsonHelper;
+
+namespace HiTeachCE.Context
+{
+    
+        public class HttpGlobalExceptionInvoke
+        {
+            private readonly RequestDelegate _next;
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="next"></param>
+            public HttpGlobalExceptionInvoke(RequestDelegate next)
+            {
+                _next = next;
+            }
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="context"></param>
+            /// <returns></returns>
+            public async Task Invoke(HttpContext context)
+            {
+                int bizCode = 0;
+                System.Exception exs = null;
+                bool isCatched = false;
+                try
+                {
+                    await _next(context);
+                }
+                catch (System.Exception ex) //发生异常
+                {
+                    exs = ex;
+                    //自定义业务异常
+                    if (ex is BizException)
+                    {
+                        bizCode = ((BizException)ex).code;
+                        if (bizCode == 401 || bizCode == 404 || bizCode == 502 || bizCode == 403)
+                        {
+                            context.Response.StatusCode = bizCode;
+                        }
+                        else
+                        {
+                            context.Response.StatusCode = 200;
+                        }
+                        // context.Response.StatusCode = ((BizException)ex).code;
+                    }
+                    //未知异常
+                    else
+                    {
+                        context.Response.StatusCode = 500;
+                        //LogHelper.SetLog(LogLevel.Error, ex);
+                    }
+                    await HandleExceptionAsync(context, bizCode, ex.Message, ex.StackTrace);
+                    isCatched = true;
+                }
+                finally
+                {
+                    if (!isCatched && context.Response.StatusCode != 200)//未捕捉过并且状态码不为200
+                    {
+                        string msg;
+                        switch (context.Response.StatusCode)
+                        {
+                            case 400:
+                                msg = "Bad Request";
+                                break;
+                            case 401:
+                                msg = "Unauthorized";
+                                break;
+                            case 404:
+                                msg = "Service Not Found";
+                                break;
+                            case 502:
+                                msg = "Request Erro";
+                                break;
+                            case 500:
+                                msg = exs.Message;
+                                break;
+                            case 403:
+                               msg = "金钥验证错误";
+                                break;
+                            default:
+                                msg = "Unknown Error";
+                                break;
+                        }
+                        await HandleExceptionAsync(context, bizCode, msg, exs == null ? "" : exs.StackTrace);
+                    }
+                }
+            }
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="context"></param>
+            /// 
+            /// 
+            /// <param name="statusCode"></param>
+            /// <param name="msg"></param>
+            /// <returns></returns>
+            private static async Task HandleExceptionAsync(HttpContext context, int bizCode, string msg, string devmsg)
+            {
+            if (context.Response.StatusCode == 500) {
+                context.Response.StatusCode = 200;
+            }
+                var data = new ErrorResponse<string>(bizCode, msg, devmsg);
+                context.Response.ContentType = Constant.CONTENT_TYPE_JSON;
+                //if (context.Response.HasStarted) {
+                await context.Response.WriteAsync(JsonNetHelper.ToJson(data));
+                //}
+
+            }
+            /// <summary>
+            /// 异常信息封装
+            /// </summary>
+            /// 
+
+            public class ErrorResponse<T>
+            {
+                public ErrorResponse()
+                {
+                    error = new ErrorModel<T>();
+                }
+                public ErrorResponse(string message)
+                {
+                    error = new ErrorModel<T>
+                    {
+                        message = message,
+                        devmsg = message
+                    };
+                }
+                public ErrorResponse(int code, string message)
+                {
+                    error = new ErrorModel<T>
+                    {
+                        message = message,
+                        devmsg = message,
+                        code = code
+                    };
+
+                }
+                public ErrorResponse(int code, string message, string devMessage)
+                {
+                    error = new ErrorModel<T>
+                    {
+                        message = message,
+                        devmsg = message,
+                        code = code
+                    };
+                    error.devmsg = devMessage;
+                }
+
+                public string jsonrpc { get; set; } = "2.0";
+                public double id { get; set; } = 1;
+                private object result { get; set; } = null;
+                public ErrorModel<T> error { get; set; } = null;
+            }
+        }
+
+        public class ErrorModel<E>
+        {
+            public long responseTime = DateTime.Now.Ticks;
+            public int code { get; set; } = 1;
+            public string message { get; set; }
+            public string devmsg { get; set; }
+            public E data { get; set; }
+        }
+ 
+}

+ 11 - 0
CMS/Context/IBusinessService.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Context
+{
+    public interface IBusinessService
+    {
+    }
+}

+ 424 - 0
CMS/Context/SourceDBContext.cs

@@ -0,0 +1,424 @@
+using CMS.Context;
+using MySql.Data.MySqlClient;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Extension.DataResult.PageToken;
+
+namespace HiTeachCE.Context
+{
+    public class SourceDBContext<Entity> where Entity : class, new()
+    {
+        public SqlSugarClient Db;
+        /// <summary>
+        /// 修改后的代码
+        /// </summary>
+        /// <returns></returns>
+        public static SourceDBContext<Entity> OpDB()
+        {
+            SourceDBContext<Entity> dbcontext_t = new SourceDBContext<Entity>();
+            dbcontext_t.Db = new SqlSugarClient(new ConnectionConfig()
+            {
+                ConnectionString = BaseConfigModel.Configuration["DbConnection:SourceMySqlConnection"],
+                DbType = SqlSugar.DbType.MySql,
+                IsAutoCloseConnection = true,
+                InitKeyType = InitKeyType.Attribute
+            });
+            return dbcontext_t;
+        }
+        protected SourceDBContext()
+        {
+            Db = new SqlSugarClient(new ConnectionConfig()
+            {
+                ConnectionString = BaseConfigModel.Configuration["DbConnection:SourceMySqlConnection"],
+                DbType = SqlSugar.DbType.MySql,
+                IsAutoCloseConnection = true,
+                InitKeyType = InitKeyType.Attribute
+            });
+            //调式代码 用来打印SQL
+            Db.Aop.OnLogExecuting = (sql, pars) =>
+            {
+                Console.WriteLine(sql + "\r\n" +
+                    Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
+            };
+        }
+        public void Dispose()
+        {
+            if (Db != null)
+            {
+                Db.Dispose();
+            }
+        }
+        public SimpleClient<Entity> CurrentDb { get { return new SimpleClient<Entity>(Db); } }
+
+        /// <summary>
+        /// 获取所有
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetList()
+        {
+            try
+            {
+                return CurrentDb.GetList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据表达式查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetListIn(Expression<Func<Entity, object>> inExpression = null, dynamic[] parmas = null)
+        {
+            try
+            {
+                if (inExpression == null || parmas == null)
+                {
+                    return null;
+                }
+                else
+                {
+                    return Db.Queryable<Entity>().In(inExpression, parmas).ToList();
+                }
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }/// <summary>
+         /// 根据表达式查询
+         /// </summary>
+         /// <returns></returns>
+        public virtual List<Entity> GetList(Expression<Func<Entity, bool>> whereExpression)
+        {
+            try
+            {
+                return Db.Queryable<Entity>().Where(whereExpression).ToList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据表达式查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetListWhereIn(Expression<Func<Entity, bool>> whereExpression = null, Expression<Func<Entity, object>> inExpression = null, dynamic[] parmas = null)
+        {
+            try
+            {
+                if (whereExpression == null && (inExpression == null || parmas == null))
+                {
+                    return null;
+                }
+
+                else if (whereExpression == null && inExpression != null && parmas != null)
+                {
+                    return Db.Queryable<Entity>().In(inExpression, parmas).ToList();
+                }
+                else
+                {
+                    if (whereExpression != null && (inExpression == null || parmas == null))
+                    {
+                        return Db.Queryable<Entity>().Where(whereExpression).ToList();
+                    }
+                    else
+                    {
+                        return Db.Queryable<Entity>().Where(whereExpression).In(inExpression, parmas).ToList();
+                    }
+                }
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+
+        }
+
+
+        /// <summary>
+        /// 根据表达式查询分页
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, Pagination pagination)
+        {
+            try
+            {
+                int total = 0;
+                List<Entity> list = Db.Queryable<Entity>().Where(whereExpression).ToPageList(pagination.currPage, pagination.pageSize, ref total);
+                pagination.total = total;
+                pagination.totalPage = (int)Math.Ceiling((double)pagination.total / (double)pagination.pageSize);
+                return list;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据表达式查询分页
+        /// </summary>
+        /// <returns></returns>
+        //public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, PageModel pageModel)
+        //{
+        //    return CurrentDb.GetPageList(whereExpression, pageModel);
+        //}
+
+        /// <summary>
+        /// 根据表达式查询分页并排序
+        /// </summary>
+        /// <param name="whereExpression">it</param>
+        /// <param name="pageModel"></param>
+        /// <param name="orderByExpression">it=>it.id或者it=>new{it.id,it.name}</param>
+        /// <param name="orderByType">OrderByType.Desc</param>
+        /// <returns></returns>
+        public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, Pagination pagination, Expression<Func<Entity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
+        {
+            try
+            {
+                int total = 0;
+                List<Entity> list = Db.Queryable<Entity>().Where(whereExpression).OrderBy(orderByExpression, orderByType).ToPageList(pagination.currPage, pagination.pageSize, ref total);
+                pagination.total = total;
+                pagination.totalPage = (int)Math.Ceiling((double)pagination.total / (double)pagination.pageSize);
+                return list;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+
+        }
+
+
+        /// <summary>
+        /// 根据主键查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual Entity GetById(dynamic id)
+        {
+            try
+            {
+                return CurrentDb.GetById(id);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据主键查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetByIds(dynamic[] ids)
+        {
+            try
+            {
+                return Db.Queryable<Entity>().In("id", ids).ToList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据主键删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(dynamic id)
+        {
+            if (string.IsNullOrEmpty(id))
+            {
+                Console.WriteLine(string.Format("要删除的主键id不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.DeleteById(id);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据实体删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(Entity data)
+        {
+            if (data == null)
+            {
+                Console.WriteLine(string.Format("要删除的实体对象不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.Delete(data);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据主键删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(dynamic[] ids)
+        {
+            if (ids.Count() <= 0)
+            {
+                Console.WriteLine(string.Format("要删除的主键ids不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.AsDeleteable().In(ids).ExecuteCommand() > 0;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据表达式删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(Expression<Func<Entity, bool>> whereExpression)
+        {
+            try
+            {
+                return CurrentDb.Delete(whereExpression);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据实体更新,实体需要有主键
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Update(Entity obj)
+        {
+            if (obj == null)
+            {
+                Console.WriteLine(string.Format("要更新的实体不能为空,必须带上主键!"));
+            }
+            try
+            {
+                return CurrentDb.Update(obj);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+
+        /// <summary>
+        ///批量更新
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Update(List<Entity> objs)
+        {
+            if (objs.Count <= 0)
+            {
+                Console.WriteLine(string.Format("要批量更新的实体不能为空,必须带上主键!"));
+            }
+            try
+            {
+                return CurrentDb.UpdateRange(objs);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 插入
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Insert(Entity obj)
+        {
+            try { 
+           
+                return CurrentDb.Insert(obj);
+            }
+            catch (MySqlException ex)
+            { 
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s= ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s+"重复!", 2);
+                }
+                else {
+                    throw new BizException(ex.Message, 2);
+                }
+                
+            }
+        }
+
+
+        /// <summary>
+        /// 批量
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Insert(List<Entity> objs)
+        {
+            try
+            {
+                return CurrentDb.InsertRange(objs);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+    }
+}

+ 425 - 0
CMS/Context/TargetDBContext.cs

@@ -0,0 +1,425 @@
+using MySql.Data.MySqlClient;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Extension.DataResult.PageToken;
+
+namespace CMS.Context
+{
+    public class TargetDBContext<Entity> where Entity : class, new()
+    {
+        public SqlSugarClient Db;
+        /// <summary>
+        /// 修改后的代码
+        /// </summary>
+        /// <returns></returns>
+        public static TargetDBContext<Entity> OpDB()
+        {
+            TargetDBContext<Entity> dbcontext_t = new TargetDBContext<Entity>();
+            dbcontext_t.Db = new SqlSugarClient(new ConnectionConfig()
+            {
+                ConnectionString = BaseConfigModel.Configuration["DbConnection:TargetMySqlConnection"],
+                DbType = SqlSugar.DbType.MySql,
+                IsAutoCloseConnection = true,
+                InitKeyType = InitKeyType.Attribute
+            });
+            return dbcontext_t;
+        }
+        protected TargetDBContext()
+        {
+            Db = new SqlSugarClient(new ConnectionConfig()
+            {
+                ConnectionString = BaseConfigModel.Configuration["DbConnection:TargetMySqlConnection"],
+                DbType = SqlSugar.DbType.MySql,
+                IsAutoCloseConnection = true,
+                InitKeyType = InitKeyType.Attribute
+            });
+            //调式代码 用来打印SQL
+            Db.Aop.OnLogExecuting = (sql, pars) =>
+            {
+                Console.WriteLine(sql + "\r\n" +
+                    Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
+            };
+        }
+        public void Dispose()
+        {
+            if (Db != null)
+            {
+                Db.Dispose();
+            }
+        }
+        public SimpleClient<Entity> CurrentDb { get { return new SimpleClient<Entity>(Db); } }
+
+        /// <summary>
+        /// 获取所有
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetList()
+        {
+            try
+            {
+                return CurrentDb.GetList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据表达式查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetListIn(Expression<Func<Entity, object>> inExpression = null, dynamic[] parmas = null)
+        {
+            try
+            {
+                if (inExpression == null || parmas == null)
+                {
+                    return null;
+                }
+                else
+                {
+                    return Db.Queryable<Entity>().In(inExpression, parmas).ToList();
+                }
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }/// <summary>
+         /// 根据表达式查询
+         /// </summary>
+         /// <returns></returns>
+        public virtual List<Entity> GetList(Expression<Func<Entity, bool>> whereExpression)
+        {
+            try
+            {
+                return Db.Queryable<Entity>().Where(whereExpression).ToList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据表达式查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetListWhereIn(Expression<Func<Entity, bool>> whereExpression = null, Expression<Func<Entity, object>> inExpression = null, dynamic[] parmas = null)
+        {
+            try
+            {
+                if (whereExpression == null && (inExpression == null || parmas == null))
+                {
+                    return null;
+                }
+
+                else if (whereExpression == null && inExpression != null && parmas != null)
+                {
+                    return Db.Queryable<Entity>().In(inExpression, parmas).ToList();
+                }
+                else
+                {
+                    if (whereExpression != null && (inExpression == null || parmas == null))
+                    {
+                        return Db.Queryable<Entity>().Where(whereExpression).ToList();
+                    }
+                    else
+                    {
+                        return Db.Queryable<Entity>().Where(whereExpression).In(inExpression, parmas).ToList();
+                    }
+                }
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+
+        }
+
+
+        /// <summary>
+        /// 根据表达式查询分页
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, Pagination pagination)
+        {
+            try
+            {
+                int total = 0;
+                List<Entity> list = Db.Queryable<Entity>().Where(whereExpression).ToPageList(pagination.currPage, pagination.pageSize, ref total);
+                pagination.total = total;
+                pagination.totalPage = (int)Math.Ceiling((double)pagination.total / (double)pagination.pageSize);
+                return list;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据表达式查询分页
+        /// </summary>
+        /// <returns></returns>
+        //public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, PageModel pageModel)
+        //{
+        //    return CurrentDb.GetPageList(whereExpression, pageModel);
+        //}
+
+        /// <summary>
+        /// 根据表达式查询分页并排序
+        /// </summary>
+        /// <param name="whereExpression">it</param>
+        /// <param name="pageModel"></param>
+        /// <param name="orderByExpression">it=>it.id或者it=>new{it.id,it.name}</param>
+        /// <param name="orderByType">OrderByType.Desc</param>
+        /// <returns></returns>
+        public virtual List<Entity> GetPageList(Expression<Func<Entity, bool>> whereExpression, Pagination pagination, Expression<Func<Entity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
+        {
+            try
+            {
+                int total = 0;
+                List<Entity> list = Db.Queryable<Entity>().Where(whereExpression).OrderBy(orderByExpression, orderByType).ToPageList(pagination.currPage, pagination.pageSize, ref total);
+                pagination.total = total;
+                pagination.totalPage = (int)Math.Ceiling((double)pagination.total / (double)pagination.pageSize);
+                return list;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+
+        }
+
+
+        /// <summary>
+        /// 根据主键查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual Entity GetById(dynamic id)
+        {
+            try
+            {
+                return CurrentDb.GetById(id);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+        /// <summary>
+        /// 根据主键查询
+        /// </summary>
+        /// <returns></returns>
+        public virtual List<Entity> GetByIds(dynamic[] ids)
+        {
+            try
+            {
+                return Db.Queryable<Entity>().In("id", ids).ToList();
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据主键删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(dynamic id)
+        {
+            if (string.IsNullOrEmpty(id))
+            {
+                Console.WriteLine(string.Format("要删除的主键id不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.DeleteById(id);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据实体删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(Entity data)
+        {
+            if (data == null)
+            {
+                Console.WriteLine(string.Format("要删除的实体对象不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.Delete(data);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据主键删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(dynamic[] ids)
+        {
+            if (ids.Count() <= 0)
+            {
+                Console.WriteLine(string.Format("要删除的主键ids不能为空值!"));
+            }
+            try
+            {
+                return CurrentDb.AsDeleteable().In(ids).ExecuteCommand() > 0;
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+        /// <summary>
+        /// 根据表达式删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Delete(Expression<Func<Entity, bool>> whereExpression)
+        {
+            try
+            {
+                return CurrentDb.Delete(whereExpression);
+            }
+            catch (MySqlException ex)
+            {
+                throw new BizException(ex.Message, 2);
+            }
+        }
+
+
+        /// <summary>
+        /// 根据实体更新,实体需要有主键
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Update(Entity obj)
+        {
+            if (obj == null)
+            {
+                Console.WriteLine(string.Format("要更新的实体不能为空,必须带上主键!"));
+            }
+            try
+            {
+                return CurrentDb.Update(obj);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+
+        /// <summary>
+        ///批量更新
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Update(List<Entity> objs)
+        {
+            if (objs.Count <= 0)
+            {
+                Console.WriteLine(string.Format("要批量更新的实体不能为空,必须带上主键!"));
+            }
+            try
+            {
+                return CurrentDb.UpdateRange(objs);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 插入
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Insert(Entity obj)
+        {
+            try
+            {
+
+                return CurrentDb.Insert(obj);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+
+            }
+        }
+
+
+        /// <summary>
+        /// 批量
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public virtual bool Insert(List<Entity> objs)
+        {
+            try
+            {
+                return CurrentDb.InsertRange(objs);
+            }
+            catch (MySqlException ex)
+            {
+                if (ex.Message.StartsWith("Duplicate entry"))
+                {
+                    string s = ex.Message.Replace("Duplicate entry", "").Split("for key")[0];
+                    throw new BizException(s + "重复!", 2);
+                }
+                else
+                {
+                    throw new BizException(ex.Message, 2);
+                }
+            }
+        }
+    }
+}

+ 25 - 0
CMS/Controllers/EtlController.cs

@@ -0,0 +1,25 @@
+using CMS.Services;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse;
+
+namespace CMS.Controllers
+{
+    [Route("api/[controller]")]
+    [ApiController]
+    public class EtlController: Controller
+    {
+        private readonly DataETLService dataETLService;
+        public EtlController(DataETLService _dataETLService) {
+            dataETLService = _dataETLService;
+        }
+        [HttpGet("list")]
+        public BaseJosnRPCResponse List() {
+            JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
+            return builder.Data(dataETLService.ETL()).build();
+        }
+    }
+}

+ 27 - 0
CMS/Extension/Captcha/HeiCaptchaExtension.cs

@@ -0,0 +1,27 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HiTeachCE.Extension.Captcha
+{
+    public static class HeiCaptchaExtension
+    {
+        /// <summary>
+        /// 启用HeiCaptcha
+        /// </summary>
+        /// <param name="services"></param>
+        /// <returns></returns>
+        public static IServiceCollection AddHeiCaptcha(this IServiceCollection services)
+        {
+            if (services == null)
+            {
+                throw new ArgumentNullException(nameof(services));
+            }
+
+            services.AddScoped<SecurityCodeHelper>();
+            return services;
+        }
+    }
+}

+ 30 - 0
CMS/Extension/Captcha/ImageRgba32Extension.cs

@@ -0,0 +1,30 @@
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.PixelFormats;
+using System;
+using System.Collections.Generic;
+using System.IO;
+namespace HiTeachCE.Extension.Captcha
+{
+    public static class ImageRgba32Extension
+    {
+        public static byte[] ToPngArray<TPixel>(this Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
+        {
+            using (var ms = new MemoryStream())
+            {
+                img.Save(ms, PngFormat.Instance);
+                return ms.ToArray();
+            }
+        }
+
+        public static byte[] ToGifArray<TPixel>(this Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
+        {
+            using (var ms = new MemoryStream())
+            {
+                img.Save(ms, new GifEncoder());
+                return ms.ToArray();
+            }
+        }
+    }
+}

+ 217 - 0
CMS/Extension/Captcha/ImageSharpExtension.cs

@@ -0,0 +1,217 @@
+using SixLabors.Fonts;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.Primitives;
+using SixLabors.Shapes;
+using System;
+using System.Collections.Generic;
+namespace HiTeachCE.Extension.Captcha
+{
+    public static class ImageSharpExtension
+    {
+
+        /// <summary>
+        /// 绘制中文字符(可以绘制字母数字,但样式可能需要改)
+        /// </summary>
+        /// <typeparam name="TPixel"></typeparam>
+        /// <param name="processingContext"></param>
+        /// <param name="containerWidth"></param>
+        /// <param name="containerHeight"></param>
+        /// <param name="text"></param>
+        /// <param name="color"></param>
+        /// <param name="font"></param>
+        /// <returns></returns>
+        public static IImageProcessingContext<TPixel> DrawingCnText<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, string text, Rgba32 color, Font font)
+              where TPixel : struct, IPixel<TPixel>
+        {
+            return processingContext.Apply(img =>
+            {
+                if (string.IsNullOrEmpty(text) == false)
+                {
+                    Random random = new Random();
+                    var textWidth = (img.Width / text.Length);
+                    var img2Size = Math.Min(textWidth, img.Height);
+                    var fontMiniSize = (int)(img2Size * 0.6);
+                    var fontMaxSize = (int)(img2Size * 0.95);
+
+                    for (int i = 0; i < text.Length; i++)
+                    {
+                        using (Image<Rgba32> img2 = new Image<Rgba32>(img2Size, img2Size))
+                        {
+                            Font scaledFont = new Font(font, random.Next(fontMiniSize, fontMaxSize));
+                            var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2);
+                            var textGraphicsOptions = new TextGraphicsOptions(true)
+                            {
+                                HorizontalAlignment = HorizontalAlignment.Left,
+                                VerticalAlignment = VerticalAlignment.Top
+                            };
+
+                            img2.Mutate(ctx => ctx
+                                .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, color, new Point(0, 0))
+                                .Rotate(random.Next(-45, 45))
+                            );
+                            img.Mutate(ctx => ctx.DrawImage(img2, point, 1));
+                        }
+                    }
+                }
+            });
+        }
+
+        public static IImageProcessingContext<TPixel> DrawingEnText<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, string text, string[] colorHexArr, Font[] fonts)
+            where TPixel : struct, IPixel<TPixel>
+        {
+            return processingContext.Apply(img =>
+            {
+                if (string.IsNullOrEmpty(text) == false)
+                {
+                    Random random = new Random();
+                    var textWidth = (img.Width / text.Length);
+                    var img2Size = Math.Min(textWidth, img.Height);
+                    var fontMiniSize = (int)(img2Size * 0.9);
+                    var fontMaxSize = (int)(img2Size * 1.37);
+                    Array fontStyleArr = Enum.GetValues(typeof(FontStyle));
+
+                    for (int i = 0; i < text.Length; i++)
+                    {
+                        using (Image<Rgba32> img2 = new Image<Rgba32>(img2Size, img2Size))
+                        {
+                            Font scaledFont = new Font(fonts[random.Next(0, fonts.Length)], random.Next(fontMiniSize, fontMaxSize), (FontStyle)fontStyleArr.GetValue(random.Next(fontStyleArr.Length)));
+                            var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2);
+                            var colorHex = colorHexArr[random.Next(0, colorHexArr.Length)];
+                            var textGraphicsOptions = new TextGraphicsOptions(true)
+                            {
+                                HorizontalAlignment = HorizontalAlignment.Left,
+                                VerticalAlignment = VerticalAlignment.Top
+                            };
+
+                            img2.Mutate(ctx => ctx
+                                            .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, Rgba32.FromHex(colorHex), new Point(0, 0))
+                                            .DrawingGrid(containerWidth, containerHeight, Rgba32.FromHex(colorHex), 6, 1)
+                                            .Rotate(random.Next(-45, 45))
+                                        );
+                            img.Mutate(ctx => ctx.DrawImage(img2, point, 1));
+                        }
+                    }
+                }
+            });
+        }
+
+        /// <summary>
+        /// 画圆圈(泡泡)
+        /// </summary>
+        /// <typeparam name="TPixel"></typeparam>
+        /// <param name="processingContext"></param>
+        /// <param name="containerWidth"></param>
+        /// <param name="containerHeight"></param>
+        /// <param name="count"></param>
+        /// <param name="miniR"></param>
+        /// <param name="maxR"></param>
+        /// <param name="color"></param>
+        /// <param name="canOverlap"></param>
+        /// <returns></returns>
+        public static IImageProcessingContext<TPixel> DrawingCircles<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, int count, int miniR, int maxR, TPixel color, bool canOverlap = false)
+               where TPixel : struct, IPixel<TPixel>
+        {
+            return processingContext.Apply(img =>
+            {
+                EllipsePolygon ep = null;
+                Random random = new Random();
+                PointF tempPoint = new PointF();
+                List<PointF> points = new List<PointF>();
+
+                if (count > 0)
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        if (canOverlap)
+                        {
+                            tempPoint = new PointF(random.Next(0, containerWidth), random.Next(0, containerHeight));
+                        }
+                        else
+                        {
+                            tempPoint = getCirclePoginF(containerWidth, containerHeight, (miniR + maxR), ref points);
+                        }
+                        ep = new EllipsePolygon(tempPoint, random.Next(miniR, maxR));
+
+                        img.Mutate(ctx => ctx
+                                      .Draw(color, (float)(random.Next(94, 145) / 100.0), ep.Clip())
+                                  );
+                    }
+                }
+            });
+        }
+        /// <summary>
+        /// 画杂线
+        /// </summary>
+        /// <typeparam name="TPixel"></typeparam>
+        /// <param name="processingContext"></param>
+        /// <param name="containerWidth"></param>
+        /// <param name="containerHeight"></param>
+        /// <param name="color"></param>
+        /// <param name="count"></param>
+        /// <param name="thickness"></param>
+        /// <returns></returns>
+        public static IImageProcessingContext<TPixel> DrawingGrid<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, TPixel color, int count, float thickness)
+            where TPixel : struct, IPixel<TPixel>
+        {
+            return processingContext.Apply(img =>
+            {
+                var points = new List<PointF> { new PointF(0, 0) };
+                for (int i = 0; i < count; i++)
+                {
+                    getCirclePoginF(containerWidth, containerHeight, 9, ref points);
+                }
+                points.Add(new PointF(containerWidth, containerHeight));
+                img.Mutate(ctx => ctx
+                               .DrawLines(color, thickness, points.ToArray())
+                          );
+            });
+        }
+
+        /// <summary>
+        /// 散 随机点
+        /// </summary>
+        /// <param name="containerWidth"></param>
+        /// <param name="containerHeight"></param>
+        /// <param name="lapR"></param>
+        /// <param name="list"></param>
+        /// <returns></returns>
+        private static PointF getCirclePoginF(int containerWidth, int containerHeight, double lapR, ref List<PointF> list)
+        {
+            Random random = new Random();
+            PointF newPoint = new PointF();
+            int retryTimes = 10;
+            double tempDistance = 0;
+
+            do
+            {
+                newPoint.X = random.Next(0, containerWidth);
+                newPoint.Y = random.Next(0, containerHeight);
+                bool tooClose = false;
+                foreach (var p in list)
+                {
+                    tooClose = false;
+                    tempDistance = Math.Sqrt((Math.Pow((p.X - newPoint.X), 2) + Math.Pow((p.Y - newPoint.Y), 2)));
+                    if (tempDistance < lapR)
+                    {
+                        tooClose = true;
+                        break;
+                    }
+                }
+                if (tooClose == false)
+                {
+                    list.Add(newPoint);
+                    break;
+                }
+            }
+            while (retryTimes-- > 0);
+
+            if (retryTimes <= 0)
+            {
+                list.Add(newPoint);
+            }
+            return newPoint;
+        }
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 282 - 0
CMS/Extension/Captcha/SecurityCodeHelper.cs


+ 16 - 0
CMS/Extension/JsonRpcRequest/BaseJosnRPCRequest.cs

@@ -0,0 +1,16 @@
+
+using System;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest
+{
+    
+    public abstract class BaseJosnRPCRequest
+    {
+        public long requestTime { get; set; } = DateTime.Now.ToUniversalTime().Ticks - 621355968000000000;
+        public string jsonrpc { get; set; } = "2.0";
+        public string method { get; set; }
+        public int id { get; set; } = 1;
+        public int timeOffset { get; set; }
+        public string lang { get; set; } = "zh-CN";
+    }
+}

+ 13 - 0
CMS/Extension/JsonRpcRequest/JosnRPCRequest.cs

@@ -0,0 +1,13 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest
+{
+    
+    public class JosnRPCRequest<T>:BaseJosnRPCRequest
+    {
+        public T @params { get; set; }
+    }
+}

+ 15 - 0
CMS/Extension/JsonRpcRequest/PaginationJosnRPCRequest.cs

@@ -0,0 +1,15 @@
+
+
+using TEAMModelOS.SDK.Extension.DataResult.RequestData;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest
+{
+    public  class PaginationJosnRPCRequest<T> : BaseJosnRPCRequest
+    {
+        public PaginationJosnRPCRequest()
+        {
+            @params = new PaginationRequest<T>();
+        }
+        public PaginationRequest<T> @params { get; set; }
+    }
+}

+ 14 - 0
CMS/Extension/JsonRpcResponse/BaseJosnRPCResponse.cs

@@ -0,0 +1,14 @@
+
+using Microsoft.VisualBasic;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class BaseJosnRPCResponse 
+    {
+        public string jsonrpc { get; set; } = "2.0";
+        public double id { get; set; } = 1;
+        private object result { get; set; }
+        public object error { get; set; } = null;
+    }
+}

+ 16 - 0
CMS/Extension/JsonRpcResponse/DataJosnRPCResponse.cs

@@ -0,0 +1,16 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class DataJosnRPCResponse<T> : BaseJosnRPCResponse
+    {
+		public DataJosnRPCResponse() { 
+		  result=  new JsonRPCResult<T>();
+		}
+		public   JsonRPCResult<T> result { get; set; } 
+    }
+}

+ 16 - 0
CMS/Extension/JsonRpcResponse/ErrorJosnRPCResponse.cs

@@ -0,0 +1,16 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class ErrorJosnRPCResponse<E> : BaseJosnRPCResponse
+    {
+        public ErrorJosnRPCResponse() {
+            error = new ErrorModel<E>();
+        }
+        public new ErrorModel<E> error { get; set; }
+    }
+}

+ 13 - 0
CMS/Extension/JsonRpcResponse/ErrorModel.cs

@@ -0,0 +1,13 @@
+
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class ErrorModel<E>
+    {
+        public int code { get; set; }
+        public string message { get; set; }
+        public string devmsg { get; set; }
+        public E data { get; set; }
+    }
+}

+ 13 - 0
CMS/Extension/JsonRpcResponse/JosnRPCResponse.cs

@@ -0,0 +1,13 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class JosnRPCResponse<T>:BaseJosnRPCResponse
+    {
+        public  T result { get; set; }
+    }
+}

+ 156 - 0
CMS/Extension/JsonRpcResponse/JsonRPCResponseBuilder.cs

@@ -0,0 +1,156 @@
+using TEAMModelOS.SDK.Extension.DataResult.PageToken;
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public  class JsonRPCResponseBuilder
+    {
+        private string message="Success";
+        private string devmsg = "Error";
+        private int code = 0;
+        private object data;
+        private long total;
+        private int currPage;
+        private int pageSize;
+        private int totalPage;
+        private Dictionary<string, object> extend;
+        private Pagination page;
+        private object error=null;
+        
+        public JsonRPCResponseBuilder()
+        {
+        }
+        public JsonRPCResponseBuilder Success()
+        {
+            error = null;
+            return this;
+        }
+
+        public JsonRPCResponseBuilder Success(String message)
+        {
+            this.message = message;
+            return this;
+        }
+        public static JsonRPCResponseBuilder custom()
+        {
+            return new JsonRPCResponseBuilder();
+        }
+
+        public JsonRPCResponseBuilder Data(object data)
+        {
+            this.data = data;
+            return this;
+        }
+		public JsonRPCResponseBuilder Error(object error, string message)
+		{
+			this.code = 1;
+			this.message = message;
+			this.error = error;
+			return this;
+		}
+		public JsonRPCResponseBuilder Error(object error, int code,string message)
+		{
+			this.code = code;
+			this.message = message;
+			this.error = error;
+			return this;
+		}
+		public JsonRPCResponseBuilder Error(object error,int code)
+		{
+			this.code = code;
+			this.message = "Error";
+			this.error = error;
+			return this;
+		}
+		public JsonRPCResponseBuilder Error(object error)
+        {
+			this.code = 1;
+			this.message = "Error";
+            this.error = error;
+            return this;
+        }
+        public JsonRPCResponseBuilder Extend(Dictionary<String, object> extend)
+        {
+            this.extend = extend;
+            return this;
+        }
+  
+        public JsonRPCResponseBuilder Page(Pagination page)
+        {
+            this.pageSize = page.pageSize;
+            this.currPage = page.currPage;
+            this.total = page.total;
+            this.page = page;
+            this.totalPage = (int)Math.Ceiling((double)this.total / (double)this.pageSize);
+            return this;
+        }
+        public JsonRPCResponseBuilder totalCount(int totalCount)
+        {
+            this.total = totalCount;
+            return this;
+        }
+
+        public JsonRPCResponseBuilder CurrPage(int currPage)
+        {
+            this.currPage = currPage;
+            return this;
+        }
+
+        public JsonRPCResponseBuilder PageSize(int pageSize)
+        {
+            this.pageSize = pageSize;
+            return this;
+        }
+
+        public JsonRPCResponseBuilder TotalPage(int totalPage)
+        {
+            this.totalPage = totalPage;
+            return this;
+        }
+        public BaseJosnRPCResponse build()
+        {
+            object baseResponse= null;
+
+            if (error != null) {
+                ErrorJosnRPCResponse<object> errorJosnRPCResponse = new ErrorJosnRPCResponse<object>();
+                errorJosnRPCResponse.error.code = code;
+                errorJosnRPCResponse.error.message = message;
+                errorJosnRPCResponse.error.data = error;
+                errorJosnRPCResponse.error.devmsg = devmsg;
+                baseResponse = errorJosnRPCResponse;
+                return (BaseJosnRPCResponse)baseResponse;
+            }
+            if (this.total > 0 && this.pageSize > 0)
+            {
+                this.totalPage = (int)Math.Ceiling((double)this.total / (double)this.pageSize);
+            }
+            
+            else if (null != this.data && this.total > 0 && this.currPage > 0 && this.pageSize > 0 && this.totalPage > 0)
+            {
+                PageJosnRPCResponse<object> pageDatasResponse = new PageJosnRPCResponse<object>();
+                pageDatasResponse.result.data = this.data;
+                pageDatasResponse.result.page = new Pagination(this.total, this.currPage, this.pageSize, this.totalPage);
+                pageDatasResponse.result.extend = this.extend;
+                pageDatasResponse.result.message = message;
+                baseResponse = pageDatasResponse;
+            }
+            else if (this.data != null)
+            {
+                DataJosnRPCResponse<object> datasResponse = new DataJosnRPCResponse<object>();
+                datasResponse.result.data = this.data;
+                datasResponse.result.extend = this.extend;
+                datasResponse.result.message = message;
+                baseResponse = datasResponse;
+            }
+            else
+            {
+                baseResponse = new BaseJosnRPCResponse();
+            }
+            return (BaseJosnRPCResponse)baseResponse;
+        }
+    }
+}

+ 16 - 0
CMS/Extension/JsonRpcResponse/JsonRPCResult.cs

@@ -0,0 +1,16 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class JsonRPCResult<T>
+    {
+        public Dictionary<string, object> extend { get; set; } = null;
+        public long responseTime { get; set; } = DateTime.Now.ToUniversalTime().Ticks - 621355968000000000;
+        public T data { get; set; }
+        public string message { get; set; } = "";
+    }
+}

+ 17 - 0
CMS/Extension/JsonRpcResponse/PageJosnRPCResponse.cs

@@ -0,0 +1,17 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class PageJosnRPCResponse<T> : BaseJosnRPCResponse
+    {
+		public PageJosnRPCResponse()
+		{
+			result = new PageJsonRPCResult<T>();
+		}
+		public   PageJsonRPCResult<T> result { get; set; }
+    }
+}

+ 15 - 0
CMS/Extension/JsonRpcResponse/PageJsonRPCResult.cs

@@ -0,0 +1,15 @@
+using TEAMModelOS.SDK.Extension.DataResult.PageToken;
+
+
+namespace TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse
+{
+    
+    public class PageJsonRPCResult<T> : JsonRPCResult<T>
+    {
+        public Pagination page { get; set; }
+
+        public PageJsonRPCResult()
+        {
+        }
+    }
+}

+ 40 - 0
CMS/Extension/Jwt/BlackListJwtSecurityTokenHandler.cs

@@ -0,0 +1,40 @@
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Helper.Security.ShaHash;
+
+namespace HiTeachCE.Extension
+{
+    public class BlackListJwtSecurityTokenHandler : JwtSecurityTokenHandler
+    {
+       
+
+        public BlackListJwtSecurityTokenHandler( )
+        {
+             
+        }
+
+        public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters,
+            out SecurityToken validatedToken)
+        {
+            var claimsPrincipal = base.ValidateToken(token, validationParameters, out validatedToken);
+
+            //解析ClaimsPrincipal取出UserId、Iat和Jti
+            //具体的验证步骤有两个:
+            //- 到Redis查找该用户的Token失效时间,如果当前Token的颁发时间在此之前就是无效的;
+            //- 到Redis的黑名单里判断是否存在该Token; 
+            //通过Redis验证Token
+            string sha = ShaHashHelper.GetSHA1(token);
+            if (RedisHelper.Exists("jwt:"+sha))
+            {
+                throw new BizException("登录失效!",401);
+            }
+            return claimsPrincipal;
+        }
+    }
+}

+ 33 - 0
CMS/Extension/Jwt/ClaimModel.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Claims;
+
+namespace TEAMModelOS.SDK.Extension.JwtAuth.Models
+{
+    public class ClaimModel
+    {
+        public ClaimModel() {
+            Claims = new List<Claim>();
+            Claim = new Dictionary<string, object>();
+            Roles = new List<string>();
+        }
+
+
+        /// <summary>
+        /// 用户身份信息
+        /// </summary>
+        public List<Claim> Claims { get; set; }
+        /// <summary>
+        /// 用户身份信息
+        /// </summary>
+        public Dictionary<string ,object> Claim { get; set; }
+        /// <summary>
+        /// 用户角色信息
+        /// </summary>
+        public List<string> Roles { get; set; }
+        /// <summary>
+        /// 令牌类型
+        /// </summary>
+        public string Scope { get; set; }
+    }
+}

+ 12 - 0
CMS/Extension/Jwt/HttpConstant.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.JwtAuth.Models
+{
+    public class HttpConstant
+    {
+        public static readonly string Authorization = "Authorization";
+        public static readonly string access_token = "access_token";
+    }
+}

+ 103 - 0
CMS/Extension/Jwt/JwtAuth.cs

@@ -0,0 +1,103 @@
+using CMS.Context;
+using HiTeachCE.Helpers;
+using IdentityModel;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Helper.Security.RSACrypt;
+
+namespace HiTeachCE.Extension
+{
+    public static class JwtAuth
+    {
+        public static void Auth(this IServiceCollection services, IConfigurationSection configuration)
+        {
+            services.Configure<JwtSetting>(configuration);
+            string path = BaseConfigModel.ContentRootPath;
+            SecurityKey creds = RsaHelper.GenerateValidationKey(path + "/public.pem");
+            // 令牌验证参数
+            var tokenValidationParameters = new TokenValidationParameters
+            {
+                //NameClaimType = JwtClaimTypes.Name,
+                //RoleClaimType = JwtClaimTypes.Role,
+                ValidateIssuerSigningKey = true,
+                IssuerSigningKey = creds,
+                ValidateIssuer = true,
+                ValidIssuer = configuration["Issuer"],//发行人
+                ValidateAudience = true,
+                ValidAudience = configuration["Audience"],//订阅人
+                ValidateLifetime = true,// 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
+                //允许的服务器时间偏移量
+                ClockSkew = TimeSpan.Zero,
+                //是否要求Token的Claims中必须包含Expires
+                RequireExpirationTime = true,
+            };
+            //https://github.com/HeartofTheForce/Heart.Auth/blob/22df97c854dc48ed9fbadf0bc185a6489aa25354/Heart.Auth.Api/Setup/Extensions/SetupAuth.cs  
+            //RSA  public private
+
+            services.AddAuthentication
+                (x =>
+                {
+                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+                    x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+                })
+                .AddJwtBearer(o =>
+            {
+                ///https://blog.csdn.net/sinat_14899485/article/details/88591848 jwt 黑名单
+                //o.SecurityTokenValidators.Clear();
+                o.SecurityTokenValidators.Add(new BlackListJwtSecurityTokenHandler()); /// 自定义黑名单拦截
+                o.TokenValidationParameters = tokenValidationParameters;
+                o.Events = new JwtBearerEvents
+                {
+                    OnAuthenticationFailed = context =>
+                    {
+                        // 如果过期,则把<是否过期>添加到,返回头信息中
+                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
+                        {
+                            context.Response.Headers.Add("Token-Expired", "true");
+                        }
+                        return Task.CompletedTask;
+                    },
+                    //Url中添加access_token=[token],直接在浏览器中访问
+                    OnMessageReceived = context => {
+                        context.Token = context.Request.Query["access_token"];
+                        return Task.CompletedTask;
+                    },
+                    //URL未授权调用
+                    OnChallenge = context => {
+
+                        return Task.CompletedTask;
+                    },
+                    //在Token验证通过后调用
+                    OnTokenValidated = context => {
+                        //编写业务
+                        return Task.CompletedTask;
+                    },
+
+                };
+            });
+            if (services == null) throw new ArgumentNullException(nameof(services));
+
+            // 1【授权】、这个和上边的异曲同工,好处就是不用在controller中,写多个 roles 。
+            // 然后这么写 [Authorize(Policy = "Admin")]
+            services.AddAuthorization(options =>
+            {
+                options.AddPolicy(Constant.Role_Root, policy => policy.RequireRole("root").Build());
+                options.AddPolicy(Constant.Role_Admin, policy => policy.RequireRole("admin").Build());
+                options.AddPolicy(Constant.Role_Lecturer, policy => policy.RequireRole("lecturer").Build());
+                options.AddPolicy(Constant.Role_Learner, policy => policy.RequireRole("learner").Build());
+                options.AddPolicy(Constant.Role_RootAdmin, policy => policy.RequireRole("root", "admin").Build());
+                options.AddPolicy(Constant.Role_WebAll, policy => policy.RequireRole("root", "admin", "lecturer").Build());
+                options.AddPolicy(Constant.Role_LecturerLearner, policy => policy.RequireRole("lecturer", "learner").Build());
+                
+            });
+        }
+    }
+}

+ 11 - 0
CMS/Extension/Jwt/JwtBlackRecord.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.JwtAuth.Models
+{
+    public class JwtBlackRecord
+    {
+        public string Jti { get; set; } 
+    }
+}

+ 13 - 0
CMS/Extension/Jwt/JwtClient.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Extension.Jwt
+{
+    public class JwtClient
+    {
+        public string Name { get; set; }
+        public double Exp { get; set; }
+    }
+}

+ 97 - 0
CMS/Extension/Jwt/JwtHelper.cs

@@ -0,0 +1,97 @@
+using CMS.Context;
+using CMS.Extension.Jwt;
+using IdentityModel;
+using Microsoft.Extensions.Configuration;
+using Microsoft.IdentityModel.Tokens;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Extension.JwtAuth.Models;
+using TEAMModelOS.SDK.Helper.Common.JsonHelper;
+using TEAMModelOS.SDK.Helper.Security.RSACrypt;
+
+namespace HiTeachCE.Extension
+{
+    public class JwtHelper
+    {
+        /// <summary>
+        /// 颁发JWT Token
+        /// </summary>
+        /// <param name="claimModel"></param>
+        /// <param name="tokenModel"></param>
+        /// <returns></returns>
+        public static JwtResponse IssueJWT(ClaimModel claimModel)
+        {
+            // JwtClient jwtClient = null;
+            Extension.JwtSetting setting = BaseConfigModel.Configuration.GetSection("JwtSetting").Get<Extension.JwtSetting>();
+            JwtClient jwtClient = setting.JwtClient.Where(x => x.Name.Equals(claimModel.Scope)).First();
+            //foreach (JwtClient client in setting.JwtClient) {
+            //    if (claimModel.Scope.Equals(client.Name)) {
+            //        jwtClient = client;
+            //        break; 
+            //    }
+            //}
+            List<Claim> claims = new List<Claim>();
+            var dateTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
+            claims.AddRange(claimModel.Claims);
+            claims.Add(new Claim(JwtClaimTypes.IssuedAt, dateTime + "", ClaimValueTypes.Integer64));
+            claims.Add(new Claim(JwtClaimTypes.NotBefore, dateTime + "", ClaimValueTypes.Integer64));
+            claims.Add(new Claim(JwtClaimTypes.Expiration, dateTime + jwtClient.Exp + "", ClaimValueTypes.Integer64));
+            claims.Add(new Claim(JwtClaimTypes.Audience, setting.Audience));
+            claims.Add(new Claim(JwtClaimTypes.Issuer, setting.Issuer));
+            claims.Add(new Claim(JwtClaimTypes.Scope, claimModel.Scope));
+            claims.Add(new Claim(JwtClaimTypes.JwtId, Guid.NewGuid().ToString()));
+            //claims.AddRange(claimModel.Roles.Select(s=>new Claim(JwtClaimTypes.Role, s)));
+            //claims.AddRange(claimModel.Claims.Select(s => new Claim(ClaimTypes.Role, s)));
+            string path = BaseConfigModel.ContentRootPath;
+            var creds = RsaHelper.GenerateSigningCredentials(path + "/private.pem");
+            var jwt = new JwtSecurityToken(
+                issuer: setting.Issuer,
+                claims: claims,
+                signingCredentials: creds
+                );
+            var jwtHandler = new JwtSecurityTokenHandler();
+            return new JwtResponse
+            {
+               // Token_type = "Bearer",
+                Access_token = jwtHandler.WriteToken(jwt),
+                Scope = claimModel.Scope
+            };
+        }
+        /// <summary>
+        /// 解析jwt
+        /// </summary>
+        /// <param name="jwtStr"></param>
+        /// <returns></returns>
+        public static ClaimModel SerializeJWT(string jwtStr)
+        {
+
+            ///https://www.cnblogs.com/JacZhu/p/6837676.html#Update2.0  刷新     用户的 Token 在过期时间之内根本无法手动设置失效,随之而来的还有重放攻击等等问题
+
+
+            var jwtHandler = new JwtSecurityTokenHandler();
+            if (string.IsNullOrEmpty(jwtStr))
+            {
+                return null;
+            }
+            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
+            jwtToken.Payload.TryGetValue(JwtClaimTypes.Role, out object role);
+            ClaimModel claimModel = new ClaimModel();
+            Dictionary<string, object> claimDict = new Dictionary<string, object>();
+            foreach (Claim claim in jwtToken.Claims)
+            {
+                claimDict.TryAdd(claim.Type, claim.Value);
+            }
+            claimDict[JwtClaimTypes.Role] = role;
+            claimModel.Claim = claimDict;
+            claimModel.Claims = jwtToken.Claims.ToList();
+            return claimModel;
+        }
+    }
+}

+ 15 - 0
CMS/Extension/Jwt/JwtResponse.cs

@@ -0,0 +1,15 @@
+using HiTeachCE.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.JwtAuth.Models
+{
+    public class JwtResponse
+    {
+        public string Access_token { get; set; }
+        public string Token_type { get; set; } = "Bearer";
+        public string Token_key { get; set; } =Constant.AUTHORIZATION;
+        public string Scope { get; set; }
+    }
+}

+ 31 - 0
CMS/Extension/Jwt/JwtSetting.cs

@@ -0,0 +1,31 @@
+using CMS.Extension.Jwt;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HiTeachCE.Extension
+{
+    public class JwtSetting
+    { /// <summary>
+      /// 项目名称
+      /// </summary>
+        // public string Project { get; set; }
+        /// <summary>
+        /// JwtClient
+        /// </summary>
+        public List<JwtClient> JwtClient { get; set; }
+        /// <summary>
+        /// WT的接收对象
+        /// </summary>
+        public string Audience { get; set; }
+        /// <summary>
+        /// JWT的签发主体
+        /// </summary>
+        public string Issuer { get; set; }
+        /// <summary>
+        /// JWT Secret Key
+        /// </summary>
+        //public string SecurityKey { get; set; }
+    }
+}

+ 15 - 0
CMS/Extension/Jwt/JwtTokenOptions.cs

@@ -0,0 +1,15 @@
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Extension.JwtAuth.Models
+{
+     public  class JwtTokenOptions
+    {
+        public string Audience { get; set; }
+        public RsaSecurityKey Key { get; set; }
+        public SigningCredentials Credentials { get; set; }
+        public string Issuer { get; set; }
+    }
+}

+ 212 - 0
CMS/Extension/Jwt/RSAUtils.cs

@@ -0,0 +1,212 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+
+namespace HiTeachCE.Extension
+{
+    public static class RSAUtils
+    {
+        private const string PrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----";
+        private const string PrivateKeyFooter = "-----END RSA PRIVATE KEY-----";
+
+        public static RSA? FromPrivateKey(string key)
+        {
+            key = key.Trim();
+
+            if (!key.StartsWith(PrivateKeyHeader) || !key.EndsWith(PrivateKeyFooter))
+                throw new ArgumentException("Expect PKCS#1 PEM format key");
+
+            key = key.Substring(PrivateKeyHeader.Length, key.Length - PrivateKeyHeader.Length - PrivateKeyFooter.Length);
+
+            // Convert.FromBase64String ignores whitespace
+            byte[] keyBytes = Convert.FromBase64String(key);
+            return DecodeRSAPrivateKey(keyBytes);
+        }
+
+        //------- Parses binary ans.1 RSA private key; returns RSA ---
+        static RSA? DecodeRSAPrivateKey(byte[] privateKey)
+        {
+
+            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
+            using var mem = new MemoryStream(privateKey);
+            using var binr = new BinaryReader(mem);
+            byte bt = 0;
+            ushort twobytes = 0;
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8230)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes != 0x0102) //version number
+                return null;
+            bt = binr.ReadByte();
+            if (bt != 0x00)
+                return null;
+
+            // ------- create RSA from private key RSAParameters -----
+            var rsaParameters = new RSAParameters
+            {
+                Modulus = binr.ReadBytes(GetIntegerSize(binr)),
+                Exponent = binr.ReadBytes(GetIntegerSize(binr)),
+                D = binr.ReadBytes(GetIntegerSize(binr)),
+                P = binr.ReadBytes(GetIntegerSize(binr)),
+                Q = binr.ReadBytes(GetIntegerSize(binr)),
+                DP = binr.ReadBytes(GetIntegerSize(binr)),
+                DQ = binr.ReadBytes(GetIntegerSize(binr)),
+                InverseQ = binr.ReadBytes(GetIntegerSize(binr)),
+            };
+            return RSA.Create(rsaParameters);
+        }
+
+        private const string PublicKeyHeader = "-----BEGIN PUBLIC KEY-----";
+        private const string PublicKeyFooter = "-----END PUBLIC KEY-----";
+
+        public static RSA? FromPublicKey(string key)
+        {
+            key = key.Trim();
+
+            if (!key.StartsWith(PublicKeyHeader) || !key.EndsWith(PublicKeyFooter))
+                throw new ArgumentException("Expect PKCS#1 PEM format key");
+
+            key = key.Substring(PublicKeyHeader.Length, key.Length - PublicKeyHeader.Length - PublicKeyFooter.Length);
+
+            // Convert.FromBase64String ignores whitespace
+            byte[] keyBytes = Convert.FromBase64String(key);
+            return DecodeX509PublicKey(keyBytes);
+        }
+
+        static RSA? DecodeX509PublicKey(byte[] x509Key)
+        {
+            // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
+            byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
+            // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
+            using var mem = new MemoryStream(x509Key);
+            using var binr = new BinaryReader(mem);
+            ushort twobytes = binr.ReadUInt16();
+            switch (twobytes)
+            {
+                case 0x8130:
+                    binr.ReadByte();    //advance 1 byte
+                    break;
+                case 0x8230:
+                    binr.ReadInt16();   //advance 2 bytes
+                    break;
+                default:
+                    return null;
+            }
+
+            byte[] seq = binr.ReadBytes(15);
+            if (!CompareBytearrays(seq, seqOid))  //make sure Sequence for OID is correct
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8203)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            byte bt = binr.ReadByte();
+            if (bt != 0x00)     //expect null byte next
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8230)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            byte lowbyte = 0x00;
+            byte highbyte = 0x00;
+
+            if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
+                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
+            else if (twobytes == 0x8202)
+            {
+                highbyte = binr.ReadByte(); //advance 2 bytes
+                lowbyte = binr.ReadByte();
+            }
+            else
+                return null;
+            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
+            int modsize = BitConverter.ToInt32(modint, 0);
+
+            byte firstbyte = binr.ReadByte();
+            binr.BaseStream.Seek(-1, SeekOrigin.Current);
+
+            if (firstbyte == 0x00)
+            {   //if first byte (highest order) of modulus is zero, don't include it
+                binr.ReadByte();    //skip this null byte
+                modsize -= 1;   //reduce modulus buffer size by 1
+            }
+
+            byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
+
+            if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
+                return null;
+            int expbytes = binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
+            byte[] exponent = binr.ReadBytes(expbytes);
+
+            // ------- create RSA from public key RSAParameters -----
+            var rsaParameters = new RSAParameters
+            {
+                Modulus = modulus,
+                Exponent = exponent
+            };
+            return RSA.Create(rsaParameters);
+        }
+
+        private static int GetIntegerSize(BinaryReader binr)
+        {
+            byte bt = binr.ReadByte();
+            if (bt != 0x02)     //expect integer
+                return 0;
+            bt = binr.ReadByte();
+
+            int count;
+            if (bt == 0x81)
+                count = binr.ReadByte();    // data size in next byte
+            else
+                if (bt == 0x82)
+            {
+                byte highbyte = binr.ReadByte();
+                byte lowbyte = binr.ReadByte();
+                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
+                count = BitConverter.ToInt32(modint, 0);
+            }
+            else
+            {
+                count = bt;     // we already have the data size
+            }
+
+            while (binr.ReadByte() == 0x00)
+            {   //remove high order zeros in data
+                count -= 1;
+            }
+            binr.BaseStream.Seek(-1, SeekOrigin.Current);     //last ReadByte wasn't a removed zero, so back up a byte
+            return count;
+        }
+
+        static bool CompareBytearrays(byte[] a, byte[] b)
+        {
+            if (a.Length != b.Length)
+                return false;
+            int i = 0;
+            foreach (byte c in a)
+            {
+                if (c != b[i])
+                    return false;
+                i++;
+            }
+            return true;
+        }
+    }
+}

+ 25 - 0
CMS/Extension/Jwt/RsaHelper.cs

@@ -0,0 +1,25 @@
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HiTeachCE.Extension
+{
+    public class RsaHelper
+    {
+        public static SigningCredentials GenerateSigningCredentials(string file) {
+            var privateRSA = RSAUtils.FromPrivateKey(File.ReadAllText(file));
+            var signingKey = new RsaSecurityKey(privateRSA);
+            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
+            return signingCredentials;
+        }
+        public static SecurityKey GenerateValidationKey(string file)
+        {
+            var publicRSA = RSAUtils.FromPublicKey(File.ReadAllText(file));
+            var signingKey = new RsaSecurityKey(publicRSA);
+            return signingKey;
+        }
+    }
+}

+ 31 - 0
CMS/Extension/PageToken/Pagination.cs

@@ -0,0 +1,31 @@
+
+using System;
+using System.Collections.Generic;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.PageToken
+{
+    
+    public class Pagination
+    {
+        public long total { get; set; }
+        public int currPage { get; set; }
+        public int pageSize { get; set; } 
+        public int totalPage { get; set; }
+      
+        public Pagination() { }
+        public Pagination(long total, int currPage, int pageSize)
+        {
+            this.total = total;
+            this.currPage = currPage;
+            this.pageSize = pageSize;
+            this.totalPage = (int)Math.Ceiling((double)this.total / (double)this.pageSize);
+        }
+        public Pagination(long total, int currPage, int pageSize, int totalPage)
+        {
+            this.total = total;
+            this.currPage = currPage;
+            this.pageSize = pageSize;
+            this.totalPage = totalPage;
+        }
+    }
+}

+ 12 - 0
CMS/Extension/PageToken/PaginationData.cs

@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.PageToken
+{
+    public class PaginationData<T>:Pagination
+    {
+        public PaginationData(int currPage, int pageSize, int total) : base(currPage, pageSize, total)
+        {
+        }
+        public List<T> data { get; set; }
+    }
+}

+ 12 - 0
CMS/Extension/RequestData/BaseRequest.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.RequestData
+{
+    public class BaseRequest
+    {
+        public long requestTime { get; set; } = DateTime.Now.ToUniversalTime().Ticks - 621355968000000000;
+        public string method { get; set; }
+        public string repeatToken { get; set; }
+    }
+}

+ 10 - 0
CMS/Extension/RequestData/PaginationRequest.cs

@@ -0,0 +1,10 @@
+using TEAMModelOS.SDK.Extension.DataResult.PageToken;
+
+namespace TEAMModelOS.SDK.Extension.DataResult.RequestData
+{
+    public class PaginationRequest<T> : BaseRequest
+    {
+        public T data { get; set; }
+        public Pagination  page{ get; set; }
+    }
+}

+ 43 - 0
CMS/Helpers/CollectionHelper/CollectionHelper.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Common.CollectionHelper
+{
+    public static class CollectionHelper
+    {
+        /// <summary>
+        /// 判断集合是否为空
+        /// </summary>
+        /// <param name="collection"></param>
+        /// <returns></returns>
+        public static bool IsEmpty(this ICollection collection)
+        {
+            if (collection != null && collection.Count > 0)
+            {
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        /// <summary>
+        /// 判断集合是否不为空
+        /// </summary>
+        /// <param name="collection"></param>
+        /// <returns></returns>
+        public static bool IsNotEmpty(this ICollection collection)
+        {
+            if (collection != null && collection.Count > 0)
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

+ 43 - 0
CMS/Helpers/CollectionHelper/ObjectToDictionaryHelper.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Common.CollectionHelper
+{
+    public static class ObjectToDictionaryHelper
+    {
+        public static IDictionary<string, object> ToDictionary(this object source)
+        {
+            return source.ToDictionary<object>();
+        }
+
+        public static IDictionary<string, T> ToDictionary<T>(this object source)
+        {
+            if (source == null)
+                ThrowExceptionWhenSourceArgumentIsNull();
+
+            var dictionary = new Dictionary<string, T>();
+            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
+                AddPropertyToDictionary<T>(property, source, dictionary);
+            return dictionary;
+        }
+
+        private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
+        {
+            object value = property.GetValue(source);
+            if (IsOfType<T>(value))
+                dictionary.Add(property.Name, (T)value);
+        }
+
+        private static bool IsOfType<T>(object value)
+        {
+            return value is T;
+        }
+
+        private static void ThrowExceptionWhenSourceArgumentIsNull()
+        {
+            throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
+        }
+    }
+}

+ 36 - 0
CMS/Helpers/Constant.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HiTeachCE.Helpers
+{
+    public class Constant
+    {
+        public static string az09 = "qwertyuiopasdfghjklzxcvbnm0123456789";
+        public static string num09 = "0123456789";
+        public const string Role_WebAll = "WebAll";
+        public const string Role_Root = "root";
+        public const string Role_Admin = "admin";
+        public const string Role_Lecturer = "lecturer";
+        public const string Role_Learner = "learner";
+        public const string Role_RootAdmin = "RootAdmin";
+        public const string Role_LecturerLearner = "LecturerLearner";
+        /// <summary>
+        /// access_token
+        /// </summary>
+		public static readonly string ACCESS_TOKEN = "access_token";
+        /// <summary>
+        /// ÒýÓÃÒ³
+        /// </summary>
+		public static readonly string REFERER = "referer";
+        /// <summary>
+        /// token ÇëÇóÍ·
+        /// </summary>
+		public static readonly string AUTHORIZATION = "Authorization";
+        /// <summary>
+        /// josn¸ñʽ
+        /// </summary>
+		public static readonly string CONTENT_TYPE_JSON = "application/json";
+    }
+}

+ 25 - 0
CMS/Helpers/HttpClientHelper.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Helper.Common.JsonHelper;
+
+namespace HiTeachCE.Helpers
+{
+    public static class HttpClientHelper
+    {
+        public static async Task<string> Post(string uri,string BasicUsername,string BasicPassword,Dictionary<string,object> data) {
+            using (HttpClient client = new HttpClient())
+            {
+                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{BasicUsername}:{BasicPassword}")));
+                HttpContent httpContent = new StringContent(data.ToApiJson(), Encoding.UTF8);
+                httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+                Uri address = new Uri(uri);
+                return await client.PostAsync(address, httpContent).Result.Content.ReadAsStringAsync();//返回值
+            }
+        }
+    }
+}

+ 38 - 0
CMS/Helpers/JsonHelper/ClassSerializers.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper
+{
+    /// <summary>
+    ///   对象与二进制流的互相转换。
+    /// </summary>
+    public class ClassSerializers
+    {
+        #region 对象与二进制流的互相转换
+        /// <summary>
+        ///   将对象流转换成二进制流
+        /// </summary>
+        public static MemoryStream SerializeBinary(object request) //将对象流转换成二进制流
+        {
+            BinaryFormatter serializer = new BinaryFormatter();
+            MemoryStream memStream = new MemoryStream(); //创建一个内存流存储区
+            serializer.Serialize(memStream, request); //将对象序列化为内存流中
+            return memStream;
+        }
+        /// <summary>
+        ///   将二进制流转换成对象
+        /// </summary>
+        public static object DeSerializeBinary(MemoryStream memStream) //将二进制流转换成对象
+        {
+            memStream.Position = 0;
+            BinaryFormatter deserializer = new BinaryFormatter();
+            object newobj = deserializer.Deserialize(memStream); //将内存流反序列化为对象
+            memStream.Close(); //关闭内存流,并释放
+            return newobj;
+        }
+        #endregion
+    }
+}

+ 29 - 0
CMS/Helpers/JsonHelper/JsonApiHelper.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper
+{
+    public static class JsonApiHelper
+    {
+        static JsonSerializerOptions options = new JsonSerializerOptions()
+        {
+            WriteIndented = true,                                   //格式化json字符串
+            AllowTrailingCommas = true,                             //可以结尾有逗号
+            //IgnoreNullValues = true,                              //可以有空值,转换json去除空值属性
+            IgnoreReadOnlyProperties = true,                        //忽略只读属性
+            PropertyNameCaseInsensitive = true,                     //忽略大小写
+            //PropertyNamingPolicy = JsonNamingPolicy.CamelCase     //命名方式是默认还是CamelCase
+        };
+        public static string ToApiJson(this object input)
+        {
+            return JsonSerializer.Serialize(input, options);
+        }
+
+        public static T FromApiJson<T>(this string input)
+        {
+            return JsonSerializer.Deserialize<T>(input, options);
+        }
+    }
+}

+ 206 - 0
CMS/Helpers/JsonHelper/JsonNetHelper.cs

@@ -0,0 +1,206 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper
+{
+    public static class JsonNetHelper
+    {
+        static JsonSerializerSettings settings = new JsonSerializerSettings()
+        {
+            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+            PreserveReferencesHandling = PreserveReferencesHandling.None
+        };
+
+        /// <summary>
+        /// 使用json序列化为字符串
+        /// </summary>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制,如:"\/Date(1439335800000+0800)\/"</param>
+        /// <returns></returns>
+        public static string ToJson(this object input, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true, bool isIndented = false)
+        {
+            settings.NullValueHandling = ignoreNullValue ? Newtonsoft.Json.NullValueHandling.Ignore : NullValueHandling.Include;
+
+            if (!string.IsNullOrWhiteSpace(dateTimeFormat))
+            {
+                var jsonConverter = new List<JsonConverter>()
+                {
+                    new Newtonsoft.Json.Converters.IsoDateTimeConverter(){ DateTimeFormat = dateTimeFormat }//如: "yyyy-MM-dd HH:mm:ss"
+                };
+                settings.Converters = jsonConverter;
+            }
+
+            //no format
+            var format = isIndented ? Newtonsoft.Json.Formatting.Indented : Formatting.None;
+            var json = JsonConvert.SerializeObject(input, format, settings);
+            return json;
+        }
+        /// <summary>
+        /// 使用json序列化为字符串
+        /// </summary>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制,如:"\/Date(1439335800000+0800)\/"</param>
+        /// <returns></returns>
+        public static string ToJsonAbs(this object input, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true, bool isIndented = false)
+        {
+
+            settings = new JsonSerializerSettings
+            {
+                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+                TypeNameHandling = TypeNameHandling.All
+            };
+            settings.NullValueHandling = ignoreNullValue ? Newtonsoft.Json.NullValueHandling.Ignore : NullValueHandling.Include;
+
+            if (!string.IsNullOrWhiteSpace(dateTimeFormat))
+            {
+                var jsonConverter = new List<JsonConverter>()
+                {
+                    new Newtonsoft.Json.Converters.IsoDateTimeConverter(){ DateTimeFormat = dateTimeFormat }//如: "yyyy-MM-dd HH:mm:ss"
+                };
+                settings.Converters = jsonConverter;
+            }
+
+            //no format
+            var format = isIndented ? Newtonsoft.Json.Formatting.Indented : Formatting.None;
+            var json = JsonConvert.SerializeObject(input, format, settings);
+            return json;
+        }
+        /// <summary>
+        /// 从序列化字符串里反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="input"></param>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制</param>
+        /// <returns></returns>
+        public static T FromJsonAbs<T>(this string input, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            var settings = new JsonSerializerSettings()
+            {
+                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
+                TypeNameHandling = TypeNameHandling.All
+            };
+            settings.NullValueHandling = ignoreNullValue ? Newtonsoft.Json.NullValueHandling.Ignore : NullValueHandling.Include;
+
+            if (!string.IsNullOrWhiteSpace(dateTimeFormat))
+            {
+                var jsonConverter = new List<JsonConverter>()
+                {
+                    new Newtonsoft.Json.Converters.IsoDateTimeConverter(){ DateTimeFormat = dateTimeFormat }//如: "yyyy-MM-dd HH:mm:ss"
+                };
+                settings.Converters = jsonConverter;
+            }
+
+            return JsonConvert.DeserializeObject<T>(input, settings);
+        }
+        /// <summary>
+        /// 从序列化字符串里反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="input"></param>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制</param>
+        /// <returns></returns>
+        public static T TryFromJson<T>(this string input, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            try
+            {
+                return input.FromJson<T>(dateTimeFormat, ignoreNullValue);
+            }
+            catch
+            {
+                return default(T);
+            }
+        }
+
+
+        /// <summary>
+        /// 从字典获取对象
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="input"></param>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制</param>
+        /// <returns></returns>
+        public static T DictToObj<T>(this Dictionary<string,object> dict, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            try
+            {
+                string input= ToJson(dict);
+                return input.FromJson<T>(dateTimeFormat, ignoreNullValue);
+            }
+            catch
+            {
+                return default(T);
+            }
+        }
+        /// <summary>
+        /// 从对象获取字典
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <param name="dateTimeFormat"></param>
+        /// <param name="ignoreNullValue"></param>
+        /// <returns></returns>
+        public static Dictionary<string ,object> ObjToDict(this object obj, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            string input = ToJson(obj);
+            return input.FromJson<Dictionary<string, object>>(dateTimeFormat, ignoreNullValue);
+        }
+
+        /// <summary>
+        /// 从序列化字符串里反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="input"></param>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制</param>
+        /// <returns></returns>
+        public static T FromJson<T>(this string input, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            var settings = new JsonSerializerSettings()
+            {
+                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
+            };
+            settings.NullValueHandling = ignoreNullValue ? Newtonsoft.Json.NullValueHandling.Ignore : NullValueHandling.Include;
+
+            if (!string.IsNullOrWhiteSpace(dateTimeFormat))
+            {
+                var jsonConverter = new List<JsonConverter>()
+                {
+                    new Newtonsoft.Json.Converters.IsoDateTimeConverter(){ DateTimeFormat = dateTimeFormat }//如: "yyyy-MM-dd HH:mm:ss"
+                };
+                settings.Converters = jsonConverter;
+            }
+
+            return JsonConvert.DeserializeObject<T>(input, settings);
+        }
+        /// <summary>
+        /// 从序列化字符串里反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="input"></param>
+        /// <param name="dateTimeFormat">默认null,即使用json.net默认的序列化机制</param>
+        /// <returns></returns>
+        public static object FromJson(this string input, Type type, string dateTimeFormat = "yyyy-MM-dd HH:mm:ss", bool ignoreNullValue = true)
+        {
+            var settings = new JsonSerializerSettings()
+            {
+                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
+            };
+            if (ignoreNullValue)
+            {
+                settings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
+            }
+
+            if (!string.IsNullOrWhiteSpace(dateTimeFormat))
+            {
+                var jsonConverter = new List<JsonConverter>()
+                {
+                    new Newtonsoft.Json.Converters.IsoDateTimeConverter(){ DateTimeFormat = dateTimeFormat }//如: "yyyy-MM-dd HH:mm:ss"
+                };
+                settings.Converters = jsonConverter;
+            }
+
+            return JsonConvert.DeserializeObject(input, type, settings);
+        }
+    }
+}

+ 18 - 0
CMS/Helpers/JsonHelper/JsonPath/IJsonPathValueSystem.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper.JsonPath
+{
+    public interface IJsonPathValueSystem
+    {
+        bool HasMember(object value, string member);
+        object GetMemberValue(object value, string member);
+        IEnumerable GetMembers(object value);
+        bool IsObject(object value);
+        bool IsArray(object value);
+        bool IsPrimitive(object value);
+    }
+}

+ 105 - 0
CMS/Helpers/JsonHelper/JsonPath/JsonApiValueSystem.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper.JsonPath
+{
+    public class JsonApiValueSystem : IJsonPathValueSystem
+    {
+        public bool HasMember(object value, string member)
+        {
+            // JsonElement document = (JsonElement)value;
+            if (value is JsonElement document)
+            {
+                if (document.ValueKind is JsonValueKind.Object)
+                {
+                    return document.TryGetProperty(member, out JsonElement json);
+
+                }
+
+                if (document.ValueKind is JsonValueKind.Array)
+                {
+                    var index = ParseInt(member, -1);
+                    return index >= 0 && index < document.EnumerateArray().ToArray().Length;
+                }
+            }
+
+            return false;
+        }
+
+        public object GetMemberValue(object value, string member)
+        {
+
+            // JsonElement document = (JsonElement)value;
+            if (value is JsonElement document)
+            {
+                if (document.ValueKind is JsonValueKind.Object)
+                {
+                    return document.GetProperty(member);
+                }
+                if (document.ValueKind is JsonValueKind.Array)
+                {
+                    var index = ParseInt(member, -1);
+                    return document.EnumerateArray().ToArray()[index];
+                }
+            }
+            return null;
+        }
+
+        public IEnumerable GetMembers(object value)
+        {
+            if (value is JsonElement document)
+            {
+                return document.EnumerateObject().Select(property => property.Name);
+            }
+            return null;
+        }
+
+        public bool IsObject(object value)
+        {
+            if (value is JsonElement document)
+            {
+                if (document.ValueKind is JsonValueKind.Array)
+                {
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            return false;
+        }
+
+        public bool IsArray(object value)
+        {
+            if (value is JsonElement document)
+                if (document.ValueKind is JsonValueKind.Array)
+                {
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            return false;
+        }
+
+        public bool IsPrimitive(object value)
+        {
+            if (value is JsonElement document)
+            {
+                if (value == null)
+                {
+                    throw new ArgumentNullException("value");
+                }
+                return !(document.ValueKind is JsonValueKind.Object) && !(document.ValueKind is JsonValueKind.Array);
+            }
+            return false;
+        }
+        int ParseInt(string s, int defaultValue) => int.TryParse(s, out int result) ? result : defaultValue;
+    }
+}

+ 64 - 0
CMS/Helpers/JsonHelper/JsonPath/JsonNetValueSystem.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+using System.Linq;
+using Newtonsoft.Json.Linq;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper.JsonPath
+{
+    public class JsonNetValueSystem : IJsonPathValueSystem
+    {
+        public bool HasMember(object value, string member)
+        {
+            if (value is JObject)
+            {
+                return (value as JObject).Properties().Any(property => property.Name == member);
+            }
+
+            if (value is JArray)
+            {
+                var index = ParseInt(member, -1);
+                return index >= 0 && index < (value as JArray).Count;
+            }
+            return false;
+        }
+
+        public object GetMemberValue(object value, string member)
+        {
+            if (value is JObject)
+            {
+                var memberValue = (value as JObject)[member];
+                return memberValue;
+            }
+            if (value is JArray)
+            {
+                var index = ParseInt(member, -1);
+                return (value as JArray)[index];
+            }
+            return null;
+        }
+
+        public IEnumerable GetMembers(object value)
+        {
+            var jobject = value as JObject;
+            return jobject?.Properties().Select(property => property.Name);
+        }
+
+        public bool IsObject(object value) => value is JObject;
+
+        public bool IsArray(object value) => value is JArray;
+
+        public bool IsPrimitive(object value)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException("value");
+            }
+
+            return !(value is JObject) && !(value is JArray);
+        }
+
+        int ParseInt(string s, int defaultValue) => int.TryParse(s, out int result) ? result : defaultValue;
+        
+         
+    }
+}

+ 480 - 0
CMS/Helpers/JsonHelper/JsonPath/JsonPathContext.cs

@@ -0,0 +1,480 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
+
+namespace TEAMModelOS.SDK.Helper.Common.JsonHelper.JsonPath
+{
+    /// <summary>
+    /// 语法文档  https://www.cnblogs.com/aoyihuashao/p/8665873.html
+    /// </summary>
+    /// <param name="script"></param>
+    /// <param name="value"></param>
+    /// <param name="context"></param>
+    /// <returns></returns>
+    public delegate object JsonPathScriptEvaluator(string script, object value, string context);
+    public delegate void JsonPathResultAccumulator(object value, string[] indicies);
+
+
+    [Serializable]
+    public sealed class JsonPathNode
+    {
+        private readonly object value;
+        private readonly string path;
+
+        public JsonPathNode(object value, string path)
+        {
+            if (path == null)
+                throw new ArgumentNullException("path");
+
+            if (path.Length == 0)
+                throw new ArgumentException("path");
+
+            this.value = value;
+            this.path = path;
+        }
+
+        public object Value
+        {
+            get { return value; }
+        }
+
+        public string Path
+        {
+            get { return path; }
+        }
+
+        public override string ToString()
+        {
+            return Path + " = " + Value;
+        }
+
+        public static object[] ValuesFrom(ICollection nodes)
+        {
+            object[] values = new object[nodes != null ? nodes.Count : 0];
+
+            if (values.Length > 0)
+            {
+                Debug.Assert(nodes != null);
+
+                int i = 0;
+                foreach (JsonPathNode node in nodes)
+                    values[i++] = node.Value;
+            }
+
+            return values;
+        }
+
+        public static string[] PathsFrom(ICollection nodes)
+        {
+            string[] paths = new string[nodes != null ? nodes.Count : 0];
+
+            if (paths.Length > 0)
+            {
+                Debug.Assert(nodes != null);
+
+                int i = 0;
+                foreach (JsonPathNode node in nodes)
+                    paths[i++] = node.Path;
+            }
+
+            return paths;
+        }
+    }
+
+    public sealed class JsonPathContext
+    {
+        public static readonly JsonPathContext Default = new JsonPathContext();
+
+        private JsonPathScriptEvaluator eval;
+        private IJsonPathValueSystem system;
+
+        public JsonPathScriptEvaluator ScriptEvaluator
+        {
+            get { return eval; }
+            set { eval = value; }
+        }
+
+        public IJsonPathValueSystem ValueSystem
+        {
+            get { return system; }
+            set { system = value; }
+        }
+
+        public void SelectTo(object obj, string expr, JsonPathResultAccumulator output)
+        {
+            if (obj == null)
+                throw new ArgumentNullException("obj");
+
+            if (output == null)
+                throw new ArgumentNullException("output");
+
+            Interpreter i = new Interpreter(output, ValueSystem, ScriptEvaluator);
+
+            expr = Normalize(expr);
+
+            if (expr.Length >= 1 && expr[0] == '$') // ^\$:?
+                expr = expr.Substring(expr.Length >= 2 && expr[1] == ';' ? 2 : 1);
+
+            i.Trace(expr, obj, "$");
+        }
+
+        public JsonPathNode[] SelectNodes(object obj, string expr)
+        {
+            ArrayList list = new ArrayList();
+            SelectNodesTo(obj, expr, list);
+            return (JsonPathNode[])list.ToArray(typeof(JsonPathNode));
+        }
+
+        public IList SelectNodesTo(object obj, string expr, IList output)
+        {
+            ListAccumulator accumulator = new ListAccumulator(output != null ? output : new ArrayList());
+            SelectTo(obj, expr, new JsonPathResultAccumulator(accumulator.Put));
+            return output;
+        }
+
+        private static Regex RegExp(string pattern)
+        {
+            return new Regex(pattern, RegexOptions.ECMAScript);
+        }
+
+        private static string Normalize(string expr)
+        {
+            NormalizationSwap swap = new NormalizationSwap();
+            expr = RegExp(@"[\['](\??\(.*?\))[\]']").Replace(expr, new MatchEvaluator(swap.Capture));
+            expr = RegExp(@"'?\.'?|\['?").Replace(expr, ";");
+            expr = RegExp(@";;;|;;").Replace(expr, ";..;");
+            expr = RegExp(@";$|'?\]|'$").Replace(expr, string.Empty);
+            expr = RegExp(@"#([0-9]+)").Replace(expr, new MatchEvaluator(swap.Yield));
+            return expr;
+        }
+
+        private sealed class NormalizationSwap
+        {
+            private readonly ArrayList subx = new ArrayList(4);
+
+            public string Capture(Match match)
+            {
+                Debug.Assert(match != null);
+
+                int index = subx.Add(match.Groups[1].Value);
+                return "[#" + index.ToString(CultureInfo.InvariantCulture) + "]";
+            }
+
+            public string Yield(Match match)
+            {
+                Debug.Assert(match != null);
+
+                int index = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
+                return (string)subx[index];
+            }
+        }
+
+        public static string AsBracketNotation(string[] indicies)
+        {
+            if (indicies == null)
+                throw new ArgumentNullException("indicies");
+
+            StringBuilder sb = new StringBuilder();
+
+            foreach (string index in indicies)
+            {
+                if (sb.Length == 0)
+                {
+                    sb.Append('$');
+                }
+                else
+                {
+                    sb.Append('[');
+                    if (RegExp(@"^[0-9*]+$").IsMatch(index))
+                        sb.Append(index);
+                    else
+                        sb.Append('\'').Append(index).Append('\'');
+                    sb.Append(']');
+                }
+            }
+
+            return sb.ToString();
+        }
+
+        private static int ParseInt(string s)
+        {
+            return ParseInt(s, 0);
+        }
+
+        private static int ParseInt(string str, int defaultValue)
+        {
+            if (str == null || str.Length == 0)
+                return defaultValue;
+
+            try
+            {
+                return int.Parse(str, NumberStyles.None, CultureInfo.InvariantCulture);
+            }
+            catch (FormatException)
+            {
+                return defaultValue;
+            }
+        }
+
+        private sealed class Interpreter
+        {
+            private readonly JsonPathResultAccumulator output;
+            private readonly JsonPathScriptEvaluator eval;
+            private readonly IJsonPathValueSystem system;
+
+            private static readonly IJsonPathValueSystem defaultValueSystem = new BasicValueSystem();
+
+            private static readonly char[] colon = new char[] { ':' };
+            private static readonly char[] semicolon = new char[] { ';' };
+
+            private delegate void WalkCallback(object member, string loc, string expr, object value, string path);
+
+            public Interpreter(JsonPathResultAccumulator output, IJsonPathValueSystem valueSystem, JsonPathScriptEvaluator eval)
+            {
+                Debug.Assert(output != null);
+
+                this.output = output;
+                this.eval = eval != null ? eval : new JsonPathScriptEvaluator(NullEval);
+                this.system = valueSystem != null ? valueSystem : defaultValueSystem;
+            }
+
+            public void Trace(string expr, object value, string path)
+            {
+                if (expr == null || expr.Length == 0)
+                {
+                    Store(path, value);
+                    return;
+                }
+
+                int i = expr.IndexOf(';');
+                string atom = i >= 0 ? expr.Substring(0, i) : expr;
+                string tail = i >= 0 ? expr.Substring(i + 1) : string.Empty;
+                bool mb = system.HasMember(value, atom);
+                if (value != null && mb)
+                {
+                    Trace(tail, Index(value, atom), path + ";" + atom);
+                }
+                else if (atom.Equals("*"))
+                {
+                    Walk(atom, tail, value, path, new WalkCallback(WalkWild));
+                }
+                else if (atom.Equals(".."))
+                {
+                    Trace(tail, value, path);
+                    Walk(atom, tail, value, path, new WalkCallback(WalkTree));
+                }
+                else if (atom.Length > 2 && atom[0] == '(' && atom[atom.Length - 1] == ')') // [(exp)]
+                {
+                    Trace(eval(atom, value, path.Substring(path.LastIndexOf(';') + 1)) + ";" + tail, value, path);
+                }
+                else if (atom.Length > 3 && atom[0] == '?' && atom[1] == '(' && atom[atom.Length - 1] == ')') // [?(exp)]
+                {
+                    Walk(atom, tail, value, path, new WalkCallback(WalkFiltered));
+                }
+                else if (RegExp(@"^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$").IsMatch(atom)) // [start:end:step] Phyton slice syntax
+                {
+                    Slice(atom, tail, value, path);
+                }
+                else if (atom.IndexOf(',') >= 0) // [name1,name2,...]
+                {
+                    foreach (string part in RegExp(@"'?,'?").Split(atom))
+                        Trace(part + ";" + tail, value, path);
+                }
+            }
+
+            private void Store(string path, object value)
+            {
+                if (path != null)
+                    output(value, path.Split(semicolon));
+            }
+
+            private void Walk(string loc, string expr, object value, string path, WalkCallback callback)
+            {
+                if (system.IsPrimitive(value)) {
+                  
+                    return;
+                }
+                if (system.IsArray(value))
+                {
+                   
+
+                    if (value is JsonElement element)
+                    {
+                        IEnumerator enumerator = element.EnumerateArray().GetEnumerator();
+                        int i = 0;
+                        while (enumerator.MoveNext()) {
+                            callback(i, loc, expr, value, path);
+                            i += 1;
+                        }
+                    }
+                    else {
+                        IList list = (IList)value;
+                        for (int i = 0; i < list.Count; i++)
+                            callback(i, loc, expr, value, path);
+                    }
+
+                }
+                else if (system.IsObject(value))
+                {
+                   
+                    IEnumerable mbs = system.GetMembers(value);
+                    
+                    foreach (string key in mbs)
+                        callback(key, loc, expr, value, path);
+                }
+            }
+
+            private void WalkWild(object member, string loc, string expr, object value, string path)
+            {
+                Trace(member + ";" + expr, value, path);
+            }
+
+            private void WalkTree(object member, string loc, string expr, object value, string path)
+            {
+                object result = Index(value, member.ToString());
+                if (result != null && !system.IsPrimitive(result))
+                    Trace("..;" + expr, result, path + ";" + member);
+            }
+
+            private void WalkFiltered(object member, string loc, string expr, object value, string path)
+            {
+                object result = eval(RegExp(@"^\?\((.*?)\)$").Replace(loc, "$1"),
+                    Index(value, member.ToString()), member.ToString());
+
+                if (Convert.ToBoolean(result, CultureInfo.InvariantCulture))
+                    Trace(member + ";" + expr, value, path);
+            }
+
+            private void Slice(string loc, string expr, object value, string path)
+            {
+                IList list = value as IList;
+
+                if (list == null)
+                    return;
+
+                int length = list.Count;
+                string[] parts = loc.Split(colon);
+                int start = ParseInt(parts[0]);
+                int end = ParseInt(parts[1], list.Count);
+                int step = parts.Length > 2 ? ParseInt(parts[2], 1) : 1;
+                start = (start < 0) ? Math.Max(0, start + length) : Math.Min(length, start);
+                end = (end < 0) ? Math.Max(0, end + length) : Math.Min(length, end);
+                for (int i = start; i < end; i += step)
+                    Trace(i + ";" + expr, value, path);
+            }
+
+            private object Index(object obj, string member)
+            {
+
+                object mbv = system.GetMemberValue(obj, member);
+               
+                return mbv;
+            }
+
+            private static object NullEval(string expr, object value, string context)
+            {
+                //
+                // @ symbol in expr must be interpreted specially to resolve
+                // to value. In JavaScript, the implementation would look 
+                // like:
+                //
+                // return obj && value && eval(expr.replace(/@/g, "value"));
+                //
+
+                return null;
+            }
+        }
+
+        private sealed class BasicValueSystem : IJsonPathValueSystem
+        {
+            public bool HasMember(object value, string member)
+            {
+                if (IsPrimitive(value))
+                    return false;
+
+                IDictionary dict = value as IDictionary;
+                if (dict != null)
+                    return dict.Contains(member);
+
+                IList list = value as IList;
+                if (list != null)
+                {
+                    int index = ParseInt(member, -1);
+                    return index >= 0 && index < list.Count;
+                }
+                object obj = value as object;
+                if (obj != null)
+                {
+                    IDictionary<string,object>  dis = obj.ToDictionary();
+                    
+                    return dis.ContainsKey(member);
+                }
+                return false;
+            }
+
+            public object GetMemberValue(object value, string member)
+            {
+                if (IsPrimitive(value))
+                    throw new ArgumentException("value");
+
+                IDictionary dict = value as IDictionary;
+                if (dict != null)
+                    return dict[member];
+
+                IList list = (IList)value;
+                int index = ParseInt(member, -1);
+                if (index >= 0 && index < list.Count)
+                    return list[index];
+
+                return null;
+            }
+
+            public IEnumerable GetMembers(object value)
+            {
+                return ((IDictionary)value).Keys;
+            }
+
+            public bool IsObject(object value)
+            {
+                return value is IDictionary;
+            }
+
+            public bool IsArray(object value)
+            {
+                return value is IList;
+            }
+
+            public bool IsPrimitive(object value)
+            {
+                if (value == null)
+                    throw new ArgumentNullException("value");
+
+                return Type.GetTypeCode(value.GetType()) != TypeCode.Object;
+            }
+        }
+
+        private sealed class ListAccumulator
+        {
+            private readonly IList list;
+
+            public ListAccumulator(IList list)
+            {
+                Debug.Assert(list != null);
+
+                this.list = list;
+            }
+
+            public void Put(object value, string[] indicies)
+            {
+                list.Add(new JsonPathNode(value, JsonPathContext.AsBracketNotation(indicies)));
+            }
+        }
+    }
+}

+ 140 - 0
CMS/Helpers/PredicateExtensions.cs

@@ -0,0 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+
+namespace System.Linq.Expressions
+{
+
+    /// <summary>
+    //
+    /*
+               Expression<Func<UserInfo, bool>> exp = x => x.Status == (int)BizConst.UserStatus.Active;  
+               if(parameters.IsAuthenticated)
+               {
+                   exp = exp.And(x => x.IdentityAuth == (int) BizConst.IdentityAuthStatus.Authed && x.CareerAuth == (int) BizConst.GlobalCareerStatus.Authed);
+               }
+               if(parameters.IsVip)
+               {
+                   exp = exp.And(x => x.Vip != (int) BizConst.VipTag.NoVip);
+               }*/
+    /// </summary>
+    public static class PredicateExtensions
+    {
+        ///// <summary>
+        ///// 机关函数应用True时:单个AND有效,多个AND有效;单个OR无效,多个OR无效;混应时写在AND后的OR有效。即,设置为True时所有or语句应该放在and语句之后,否则无效
+        ///// </summary>
+        //public static Expression<Func<T, bool>> True<T>() { return f => true; }
+
+        ///// <summary>
+        ///// 机关函数应用False时:单个AND无效,多个AND无效;单个OR有效,多个OR有效;混应时写在OR后面的AND有效。 即,设置为False时所有or语句应该放在and语句之前,否则无效
+        ///// </summary>
+        //public static Expression<Func<T, bool>> False<T>() { return f => false; }
+
+        //public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1,
+        //   Expression<Func<T, bool>> expression2)
+        //{
+        //    var invokedExpression = Expression.Invoke(expression2, expression1.Parameters
+        //            .Cast<Expression>());
+
+        //    return Expression.Lambda<Func<T, bool>>(Expression.Or(expression1.Body, invokedExpression),
+        //    expression1.Parameters);
+        //}
+
+        //public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1,
+        //      Expression<Func<T, bool>> expression2)
+        //{
+        //    var invokedExpression = Expression.Invoke(expression2, expression1.Parameters
+        //         .Cast<Expression>());
+
+        //    return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body,
+        //           invokedExpression), expression1.Parameters);
+
+        //}
+
+
+        /// <summary>
+        /// 机关函数应用True时:单个AND有效,多个AND有效;单个OR无效,多个OR无效;混应时写在AND后的OR有效。即,设置为True时所有or语句应该放在and语句之后,否则无效
+        /// </summary>
+        public static Expression<Func<T, bool>> BaseAnd<T>() { return f => true; }
+        /// <summary>
+        /// 机关函数应用False时:单个AND无效,多个AND无效;单个OR有效,多个OR有效;混应时写在OR后面的AND有效。 即,设置为False时所有or语句应该放在and语句之前,否则无效
+        /// </summary>
+        public static Expression<Func<T, bool>> BaseOr<T>() { return f => false; }
+
+        public static Expression<Func<T, bool>> Or<T>(
+            this Expression<Func<T, bool>> expr1,
+            Expression<Func<T, bool>> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda<Func<T, bool>>
+                  (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        public static Expression<Func<T, bool>> And<T>(
+            this Expression<Func<T, bool>> expr1,
+            Expression<Func<T, bool>> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda<Func<T, bool>>
+                  (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        public static Expression Replace(this Expression expression,
+        Expression searchEx, Expression replaceEx)
+        {
+            return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
+        }
+
+        public static Expression<Func<T, bool>> CombineOrPreicatesWithAndPredicates<T>(this Expression<Func<T, bool>> combinedPredicate,
+            Expression<Func<T, bool>> andPredicate, Expression<Func<T, bool>> orPredicate)
+        {
+            combinedPredicate = combinedPredicate ?? BaseAnd<T>();
+            if (andPredicate != null && orPredicate != null)
+            {
+                andPredicate = andPredicate.And(orPredicate);
+                combinedPredicate = combinedPredicate.And(andPredicate);
+            }
+            else if (orPredicate != null)
+            {
+                combinedPredicate = combinedPredicate.And(orPredicate);
+            }
+            else
+            {
+                combinedPredicate = combinedPredicate.And(andPredicate);
+            }
+            return combinedPredicate;
+        }
+
+        public static void AddToPredicateTypeBasedOnIfAndOrOr<T>(ref Expression<Func<T, bool>> andPredicate,
+            ref Expression<Func<T, bool>> orPredicate, Expression<Func<T, bool>> newExpression, bool isAnd)
+        {
+            if (isAnd)
+            {
+                andPredicate = andPredicate ?? BaseAnd<T>();
+                andPredicate = andPredicate.And(newExpression);
+            }
+            else
+            {
+                orPredicate = orPredicate ?? BaseOr<T>();
+                orPredicate = orPredicate.Or(newExpression);
+            }
+        }
+    }
+    internal class ReplaceVisitor : ExpressionVisitor
+    {
+        private readonly Expression from, to;
+
+        public ReplaceVisitor(Expression from, Expression to)
+        {
+            this.from = from;
+            this.to = to;
+        }
+
+        public override Expression Visit(Expression node)
+        {
+            return node == from ? to : base.Visit(node);
+        }
+    }
+}

+ 173 - 0
CMS/Helpers/RSACrypt/RsaHelper.cs

@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using TEAMModelOS.SDK.Helper.Common.JsonHelper;
+
+namespace TEAMModelOS.SDK.Helper.Security.RSACrypt
+{
+    public static class RsaHelper
+    {
+        public static string RSASign(string data, string privateKeyPem)
+        {
+            RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPem);
+            byte[] dataBytes = Encoding.UTF8.GetBytes(data);
+            byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1");
+            return Convert.ToBase64String(signatureBytes);
+        }
+
+        private static byte[] GetPem(string type, byte[] data)
+        {
+            string pem = Encoding.UTF8.GetString(data);
+            string header = String.Format("-----BEGIN {0}-----\\n", type);
+            string footer = String.Format("-----END {0}-----", type);
+            int start = pem.IndexOf(header) + header.Length;
+            int end = pem.IndexOf(footer, start);
+            string base64 = pem.Substring(start, (end - start));
+            return Convert.FromBase64String(base64);
+        }
+        public static string LoadCertificateFileToSting(string filename)
+        {
+            FileStream fs = System.IO.File.OpenRead(filename);
+            byte[] data = new byte[fs.Length];
+            byte[] res = null;
+            fs.Read(data, 0, data.Length);
+            if (data[0] != 0x30)
+            {
+                res = GetPem("RSA PRIVATE KEY", data);
+            }
+            return res.ToJson();
+        }
+        public static RSACryptoServiceProvider LoadCertificateFile(string filename)
+        {
+            FileStream fs = System.IO.File.OpenRead(filename);
+            byte[] data = new byte[fs.Length];
+            byte[] res = null;
+            fs.Read(data, 0, data.Length);
+            if (data[0] != 0x30)
+            {
+                res = GetPem("RSA PRIVATE KEY", data);
+            }
+            string ss = res.ToJson();
+            RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);
+            return rsa;
+        }
+
+        private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
+        {
+            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
+
+            // --------- Set up stream to decode the asn.1 encoded RSA private key ------
+            MemoryStream mem = new MemoryStream(privkey);
+            BinaryReader binr = new BinaryReader(mem);  //wrap Memory Stream with BinaryReader for easy reading
+            byte bt = 0;
+            ushort twobytes = 0;
+            int elems = 0;
+            try
+            {
+                twobytes = binr.ReadUInt16();
+                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+                    binr.ReadByte();    //advance 1 byte
+                else if (twobytes == 0x8230)
+                    binr.ReadInt16();    //advance 2 bytes
+                else
+                    return null;
+
+                twobytes = binr.ReadUInt16();
+                if (twobytes != 0x0102) //version number
+                    return null;
+                bt = binr.ReadByte();
+                if (bt != 0x00)
+                    return null;
+
+
+                //------ all private key components are Integer sequences ----
+                elems = GetIntegerSize(binr);
+                MODULUS = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                E = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                D = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                P = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                Q = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                DP = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                DQ = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                IQ = binr.ReadBytes(elems);
+                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
+                CspParameters CspParameters = new CspParameters
+                {
+                    Flags = CspProviderFlags.UseMachineKeyStore
+                };
+                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters);
+                RSAParameters RSAparams = new RSAParameters
+                {
+                    Modulus = MODULUS,
+                    Exponent = E,
+                    D = D,
+                    P = P,
+                    Q = Q,
+                    DP = DP,
+                    DQ = DQ,
+                    InverseQ = IQ
+                };
+                RSA.ImportParameters(RSAparams);
+                return RSA;
+            }
+            catch (Exception ex)
+            {
+                throw new Exception("", ex);
+            }
+            finally
+            {
+                binr.Close();
+            }
+        }
+
+        private static int GetIntegerSize(BinaryReader binr)
+        {
+            byte bt = 0;
+            byte lowbyte = 0x00;
+            byte highbyte = 0x00;
+            int count = 0;
+            bt = binr.ReadByte();
+            if (bt != 0x02)        //expect integer
+                return 0;
+            bt = binr.ReadByte();
+
+            if (bt == 0x81)
+                count = binr.ReadByte();    // data size in next byte
+            else
+                if (bt == 0x82)
+            {
+                highbyte = binr.ReadByte();    // data size in next 2 bytes
+                lowbyte = binr.ReadByte();
+                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
+                count = BitConverter.ToInt32(modint, 0);
+            }
+            else
+            {
+                count = bt;        // we already have the data size
+            }
+
+            while (binr.ReadByte() == 0x00)
+            {    //remove high order zeros in data
+                count -= 1;
+            }
+            binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte
+            return count;
+        }
+    }
+}

+ 137 - 0
CMS/Helpers/ShaHash/ShaHashHelper.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Security.ShaHash
+{
+    public class ShaHashHelper
+    {
+
+        public static string GetSHA1(string Code) {
+            var resbuffer = Encoding.Default.GetBytes(Code);
+            HashAlgorithm iSha = new SHA1CryptoServiceProvider();
+            resbuffer = iSha.ComputeHash(resbuffer);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(strRes);
+        }
+        public static string GetSHA1(byte[] buffer)
+        {
+            var resbuffer = buffer;
+            HashAlgorithm iSha = new SHA1CryptoServiceProvider();
+            resbuffer = iSha.ComputeHash(buffer);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            // return Convert.ToBase64String(strRes);
+        }
+        public static string GetSHA1(Stream stream)
+        {
+            byte[] buffer = new byte[stream.Length];
+
+            stream.Read(buffer, 0, buffer.Length);
+            // stream.Close();
+            var resbuffer = buffer;
+            HashAlgorithm iSha = new SHA1CryptoServiceProvider();
+            resbuffer = iSha.ComputeHash(buffer);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            // return Convert.ToBase64String(strRes);
+        }
+        public static string GetSHA1(string Code,string SecretKey)
+        {
+            HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(SecretKey));
+            byte[] resbuffer = hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(Code));
+
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(rstRes); 
+        }
+        public static string GetSHA1(byte[] buffer, string SecretKey)
+        {
+            HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(SecretKey));
+            byte[] resbuffer = hmacsha1.ComputeHash(buffer);
+
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //eturn Convert.ToBase64String(buffer);
+        }
+        public static string GetSHA256(byte[] buffer)
+        {
+            SHA256Managed Sha256 = new SHA256Managed();
+            byte[] resbuffer = Sha256.ComputeHash(buffer);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(retval);
+
+        }
+
+        public static string GetSHA256(string Code)
+        {
+            SHA256Managed Sha256 = new SHA256Managed();
+            byte[] resbuffer = Sha256.ComputeHash(Encoding.UTF8.GetBytes(Code));
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(retval);
+        }
+
+        public static string GetSHA256(byte[] buffer, string SecretKey)
+        {
+            byte[] messageBytes = buffer;
+            byte[] keyByte = Encoding.UTF8.GetBytes(SecretKey);
+            var hmacsha256 = new HMACSHA256(keyByte);
+            byte[] resbuffer = hmacsha256.ComputeHash(messageBytes);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(hashmessage);
+        }
+
+        public static string GetSHA256(string Code, string SecretKey)
+        {
+            byte[] messageBytes = Encoding.UTF8.GetBytes(Code);
+            byte[] keyByte = Encoding.UTF8.GetBytes(SecretKey);
+            var hmacsha256 = new HMACSHA256(keyByte);
+            byte[] resbuffer = hmacsha256.ComputeHash(messageBytes);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < resbuffer.Length; i++)
+            {
+                builder.Append(resbuffer[i].ToString("x2"));
+            }
+            return builder.ToString();
+            //return Convert.ToBase64String(resbuffer);
+        }
+    }
+}

+ 94 - 0
CMS/Helpers/ToolGoodUtils.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ToolGood.Words;
+namespace HiTeachCE.Helpers
+{
+    /// <summary>
+    /// ToolGood.Words类库配合敏感库
+    /// </summary>
+    public class ToolGoodUtils
+    {
+        //敏感库只要这二个文件存在即可
+        //本地敏感库缓存- https://github.com/toolgood/ToolGood.Words/tree/master/csharp/ToolGood.Words.Test/_Illegal
+        //因为需要上传至github并同步gitee,安全起见,解压压缩包wwwroot目录下的_Illegal.zip
+        private const string KeywordsPath = "wwwroot/_Illegal/IllegalKeywords.txt";
+        private const string UrlsPath = "wwwroot/_Illegal/IllegalUrls.txt";
+
+        //更多敏感词汇   https://github.com/fighting41love/funNLP/tree/master/data/%E6%95%8F%E6%84%9F%E8%AF%8D%E5%BA%93
+
+        private const string InfoPath = "wwwroot/_Illegal/IllegalInfo.txt";
+        private const string BitPath = "wwwroot/_Illegal/IllegalBit.iws";
+
+        private static IllegalWordsSearch _search;
+        /// <summary>
+        /// 本地敏感库,文件修改后,重新创建缓存Bit
+        /// </summary>
+        /// <returns></returns>
+        public static IllegalWordsSearch GetIllegalWordsSearch()
+        {
+            if (!File.Exists(UrlsPath) || !File.Exists(KeywordsPath))
+            {
+                return new IllegalWordsSearch();
+            }
+
+            if (_search == null)
+            {
+                string ipath = Path.GetFullPath(InfoPath);
+                if (File.Exists(ipath) == false)
+                {
+                    _search = CreateIllegalWordsSearch();
+                }
+                else
+                {
+                    var texts = File.ReadAllText(ipath).Split('|');
+                    if (new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=
+                        texts[0] ||
+                        new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=
+                        texts[1]
+                    )
+                    {
+                        _search = CreateIllegalWordsSearch();
+                    }
+                    else
+                    {
+                        var s = new IllegalWordsSearch();
+                        s.Load(Path.GetFullPath(BitPath));
+                        _search = s;
+                    }
+                }
+            }
+            return _search;
+        }
+
+        private static IllegalWordsSearch CreateIllegalWordsSearch()
+        {
+            string[] words1 = File.ReadAllLines(Path.GetFullPath(KeywordsPath), Encoding.UTF8);
+            string[] words2 = File.ReadAllLines(Path.GetFullPath(UrlsPath), Encoding.UTF8);
+            var words = new List<string>();
+            foreach (var item in words1)
+            {
+                words.Add(item.Trim());
+            }
+            foreach (var item in words2)
+            {
+                words.Add(item.Trim());
+            }
+
+            var search = new IllegalWordsSearch();
+            search.SetKeywords(words);
+
+            search.Save(Path.GetFullPath(BitPath));
+
+            var text = new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + "|"
+                                                                                                                  + new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");
+            File.WriteAllText(Path.GetFullPath(InfoPath), text);
+
+            return search;
+        }
+
+    }
+}

+ 51 - 0
CMS/Models/Source/SourceExercise.cs

@@ -0,0 +1,51 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Models.Source
+{
+    [SugarTable("exercise")]
+    public class SourceExercise
+    {
+        [SugarColumn(IsNullable = false, IsPrimaryKey = true)]
+        public int ExNO { get; set; }
+        public int MemberID { get; set; }
+        public int CourseNO { get; set; }
+        public int ClassID { get; set; }
+        public int TPID { get; set; }
+        char ExType { get; set; }
+        char ExMode { get; set; }
+        public string ExName { get; set; }
+        public string ExLink { get; set; }
+       // public DateTime  ExTime { get; set; }
+        //public DateTime  EndTime { get; set; }
+        public int QNumber { get; set; }
+        public int StuCount { get; set; }
+        public int AnsNum { get; set; }
+        public int TrueNum { get; set; }
+        public float  TrueRate { get; set; }
+        public int TotalSpendTime { get; set; }
+        public float  AvgSpendTime { get; set; }
+        public string Rule { get; set; }
+        char Status { get; set; }
+        public string ExNORec { get; set; }
+        public string FilePath { get; set; }
+        public string SERIALNUMBER { get; set; }
+        public string Description { get; set; }
+        public string ReportTitle { get; set; }
+        public string ReportSubject { get; set; }
+        public int ReportGrade { get; set; }
+        public string ReportTestName { get; set; }
+       // public DateTime ReportTestDate { get; set; }
+        public float  ScoreRate { get; set; }
+        public float  StdScore { get; set; }
+        public int SchoolCount { get; set; }
+        public int ClassCount { get; set; }
+        public int ItemCount { get; set; }
+        public float  Median { get; set; }
+        public int FClass { get; set; }
+
+    }
+}

+ 50 - 0
CMS/Models/Target/TargetExercise.cs

@@ -0,0 +1,50 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Models.Target
+{
+    [SugarTable("exercise")]
+    public class TargetExercise
+    {
+        [SugarColumn(IsNullable = false, IsPrimaryKey = true)]
+        public int ExNO { get; set; }
+        public int MemberID { get; set; }
+        public int CourseNO { get; set; }
+        public int ClassID { get; set; }
+        public int TPID { get; set; }
+        //char ExType { get; set; }
+        //char ExMode { get; set; }
+        //public string ExName { get; set; }
+        //public string ExLink { get; set; }
+        //public DateTime ExTime { get; set; }
+        //public DateTime EndTime { get; set; }
+        //public int QNumber { get; set; }
+        //public int StuCount { get; set; }
+        //public int AnsNum { get; set; }
+        //public int TrueNum { get; set; }
+        //public float TrueRate { get; set; }
+        //public int TotalSpendTime { get; set; }
+        //public float AvgSpendTime { get; set; }
+        //public string Rule { get; set; }
+        //char Status { get; set; }
+        //public string ExNORec { get; set; }
+        //public string FilePath { get; set; }
+        //public string SERIALNUMBER { get; set; }
+        //public string Description { get; set; }
+        //public string ReportTitle { get; set; }
+        //public string ReportSubject { get; set; }
+        //public int ReportGrade { get; set; }
+        //public string ReportTestName { get; set; }
+        //public DateTime ReportTestDate { get; set; }
+        //public float ScoreRate { get; set; }
+        //public float StdScore { get; set; }
+        //public int SchoolCount { get; set; }
+        //public int ClassCount { get; set; }
+        //public int ItemCount { get; set; }
+        //public float Median { get; set; }
+        //public int FClass { get; set; }
+    }
+}

+ 32 - 0
CMS/Program.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace CMS
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            CreateHostBuilder(args).Build().Run();
+        }
+
+        public static IHostBuilder CreateHostBuilder(string[] args) =>
+           Host.CreateDefaultBuilder(args)
+               .ConfigureWebHostDefaults(webBuilder =>
+               {
+                   webBuilder.UseKestrel(o =>
+                   {
+                       o.ListenAnyIP(4001, listenOptions =>
+                      {
+                          listenOptions.UseHttps("habook.pfx", "habook");
+                      });
+                   }).UseStartup<Startup>();
+               });
+    }
+}

+ 30 - 0
CMS/Properties/launchSettings.json

@@ -0,0 +1,30 @@
+{
+  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:55350",
+      "sslPort": 44384
+    }
+  },
+  "profiles": {
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "CMS": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "applicationUrl": "https://localhost:5001;http://localhost:5000",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 56 - 0
CMS/Services/DataETLService.cs

@@ -0,0 +1,56 @@
+using CMS.Context;
+using CMS.Models.Source;
+using CMS.Models.Target;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Services
+{
+    public class DataETLService: IBusinessService
+    {
+        private readonly SourceService sourceService;
+        private readonly TargetService targetService;
+        public DataETLService(SourceService _sourceService,TargetService _targetService) {
+            sourceService = _sourceService;
+            targetService = _targetService;
+        }
+        /// <summary>
+        /// 抽取数据
+        /// </summary>
+        /// <returns></returns>
+        public List<SourceExercise> ExtractData() {
+          return  sourceService.GetList();
+        }
+        /// <summary>
+        /// 转换数据
+        /// </summary>
+        /// <returns></returns>
+        public List<TargetExercise> TransformData(List<SourceExercise> sourceExercises)
+        {
+            List<TargetExercise> targetExercises = new List<TargetExercise>();
+
+            sourceExercises.ForEach(x => {
+                targetExercises.Add(new TargetExercise { ExNO = x.ExNO ,MemberID=x.MemberID,CourseNO=x.CourseNO,ClassID=x.ClassID,TPID=x.TPID});
+            });
+            return targetExercises;
+        }
+        /// <summary>
+        /// 装载数据
+        /// </summary>
+        /// <returns></returns>
+        public bool LoadData(List<TargetExercise> targetExercises)
+        {
+            return targetService.Insert(targetExercises);
+        }
+
+        
+        public bool ETL() {
+            List<SourceExercise> sourceExercises=  ExtractData();
+
+            List<TargetExercise> targetExercises = TransformData(sourceExercises);
+            return LoadData(targetExercises);
+        }
+    }
+}

+ 14 - 0
CMS/Services/SourceService.cs

@@ -0,0 +1,14 @@
+using CMS.Context;
+using CMS.Models.Source;
+using HiTeachCE.Context;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Services
+{
+    public class SourceService : SourceDBContext<SourceExercise>, IBusinessService
+    {
+    }
+}

+ 13 - 0
CMS/Services/TargetService.cs

@@ -0,0 +1,13 @@
+using CMS.Context;
+using CMS.Models.Target;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CMS.Services
+{
+    public class TargetService : TargetDBContext<TargetExercise>, IBusinessService
+    {
+    }
+}

+ 71 - 0
CMS/Startup.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CMS.Context;
+using CMS.Models.Target;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using SqlSugar;
+
+namespace CMS
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration, IWebHostEnvironment env)
+        {
+            Configuration = configuration;
+            BaseConfigModel.SetBaseConfig(Configuration, env.ContentRootPath, env.WebRootPath);
+        }
+
+        public IConfiguration Configuration { get; }
+
+        // This method gets called by the runtime. Use this method to add services to the container.
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddControllers();
+            // 注册验证要求的处理器,可通过这种方式对同一种要求添加多种验证
+            //全局扫描基于IBusinessService接口的实现类
+            services.Scan(scan => scan.FromApplicationDependencies()
+               .AddClasses(classes => classes.AssignableTo<IBusinessService>())
+                   .AsSelfWithInterfaces()
+                   .WithScopedLifetime());
+        }
+
+        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            if (env.IsDevelopment())
+            {
+                app.UseDeveloperExceptionPage();
+            }
+
+            app.UseHttpsRedirection();
+
+            app.UseRouting();
+
+            app.UseAuthorization();
+
+            app.UseEndpoints(endpoints =>
+            {
+                endpoints.MapControllers();
+            });
+            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
+            {
+                ConnectionString = BaseConfigModel.Configuration["DbConnection:TargetMySqlConnection"],
+                DbType = DbType.MySql,
+                IsAutoCloseConnection = true,
+                InitKeyType = InitKeyType.Attribute
+            });
+            db.CodeFirst.InitTables(
+              typeof(TargetExercise)
+            );
+        }
+    }
+}

+ 32 - 0
CMS/appsettings.Development.json

@@ -0,0 +1,32 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "DBConnection": {
+    "SourceMySqlConnection": "server=192.168.8.116;database=learncenter;port=3306;uid=iesdata;pwd=ies80650390;charset='utf8mb4';SslMode=None",
+    "TargetMySqlConnection": "server=218.95.135.157;database=data_ys;port=6603;uid=dsj_ys;pwd=gELVDqP5l7xfOwM#;charset='utf8mb4';SslMode=None"
+  },
+  "Redis": {
+    "ConnectionString": "106.12.23.251:6379,password=habook,ssl=false,abortConnect=False,defaultDatabase=11,writeBuffer=10240,poolsize=50,prefix=CMS:"
+  },
+  "JwtSetting": {
+    "Issuer": "CMS", //Ç©·¢Õß
+    "Audience": "CMS",
+    "JwtClient": [
+      {
+        "Name": "WebApp",
+        "Project": "CMSWeb",
+        "Exp": 86400
+      },
+      {
+        "Name": "Admin",
+        "Project": "CMSAdmin",
+        "Exp": 86400
+      }
+    ]
+  }
+}

+ 10 - 0
CMS/appsettings.json

@@ -0,0 +1,10 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "AllowedHosts": "*"
+}

+ 35 - 0
CMS/log4net.config

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <!-- This section contains the log4net configuration settings -->
+  <log4net>
+    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
+      <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
+    </appender>
+    <appender name="FileAppender" type="log4net.Appender.FileAppender">
+      <!--<file value="log-file.log" />-->
+      <appendToFile value="true" />
+      <layout type="log4net.Layout.PatternLayout">
+        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
+      </layout>
+    </appender>
+    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
+      <file value="logfile/" />
+      <appendToFile value="true" />
+      <rollingStyle value="Composite" />
+      <staticLogFileName value="false" />
+      <datePattern value="yyyyMMdd'.log'" />
+      <maxSizeRollBackups value="10" />
+      <maximumFileSize value="1MB" />
+      <layout type="log4net.Layout.PatternLayout">
+        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
+      </layout>
+    </appender>
+    <!-- Setup the root category, add the appenders and set the default level -->
+    <root>
+      <level value="ALL" />
+      <appender-ref ref="ConsoleAppender" />
+      <appender-ref ref="FileAppender" />
+      <appender-ref ref="RollingLogFileAppender" />
+    </root>
+  </log4net>
+</configuration>

+ 27 - 0
CMS/private.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA478XskPJlQaL216X0N/XkG23OKmPDJkorDVb7V4kLF48+zoo
+8SAZMipNPiQo4nMXMjERk8YYrgZzzCESfESiEnWtMpqCFKShTWvqDDBqxoS/61xv
+RmAVSTfgNiGTJSAgnStZ5qJfLFYjf10wy2N2rL1PT8K0mg48U50teiUApdOlM9Lx
+KvCscpwKSehvDcrM3gcp6QfCzZMPf/carA8lL8l0Ql/F+cjtBGIPKWMgHsm+70i0
+LZjYyJcFJUsbZW+0LTFVA6/JG7lsNDRpmn6m9REfJXgbQqNar8KNMNApM34BkzKz
+O88IWy/Kn3PRR2FPsxUJ3uIJt5yOCUYP2BtdgwIDAQABAoIBAQDVJctvs7G+H9pU
+/Tro6hY9vfF0vnx7Nfyy712R0kHYpHo+Rjh7M6dhI+YW+pCpHz3eY74np4cBmFhX
++7vpQfLNhAUNDz4fQ9UTOKRbtBS6pxNXm7MpElPZqsnU36dvX5omfqQtDlo0jIm8
+ceNw9y3ijWrlIz0T0a70Mm6Vmnv4tU/JDfJ5vmsn0cS8WavYn5n5UovNE6JOdR2a
+1vzSHcmIj0m/mtzRPAM46r2+NDeCMG90ZLRuD4WYeN5ob27DwazQXvVADqfexZET
+U+ECFNG70EvY2kOBqLGED0hfKmXLzckAU9UyCaWUF99JxoOjdH9HTbSjxr3wBmke
++uDTNNABAoGBAPw84m0tMy1PnkjYsQdCvcAhqDGXLdOy6GDNCl/GlWcF2LLile2x
+4wpLpOEm15DW/i7mtCc+eqq2ngVXpcnVH5EKHIE+SY1t1CXYkJvN/v802fVVSkd+
+JJW/HwgVnnK4s67atrx3BgBMGYYisA/AGwPWzeupHEne7jvDqimEltCDAoGBAOck
+sW1rwYy71FVM5RlAKw5vUlxS7BE4STNS18vJe+XfCyg4M8W7fX2HoYncihESzkKm
+KbuiidjTcJs6RaBq0HxHhv8/sKjO3LG8apbH5mFDIO1W4a4e7kc1FalOq3cDVwIG
+dncHK0ymsAOA+yCMNznhf8wqPST0vmCKTwDyKa8BAoGBAPmd5xXUHUlB+YptpwNg
+cReqNyCcU6Wk74KcZx/RDhkeGA0vXuATonOV2F1YawvTN0iC1tXfZtV6U3dF/bN3
+Tf3i28KrOW7UuZWac8E8YpV8YBYBibimhN4MfVEq09sEHg10NFLeFvpEVR4BRerQ
+Weu6r53/hRc1nt1WDRd5NyaxAoGAIT28qoDRr/yfN7k8RVpeFtBZpt9iBcPzewcR
+88PBJrjh8OHMSEaDcJcd2ya1UGlE8n7VB6ADdQRLcHd75esWmpjqyDCPpmdBg+oV
+5iNPdXNi+97/y7u1BtaSi+u9avs2+xqU1N9aEcbzDz3wX6jqlE9iwqjcbEEqU9Xw
+MLGi3wECgYAbiT2z/eN+Q6+eT7yPbUxTXcm3NexfMzTVADkNxX5uwSwgs6Jlk+HR
+/mXCwmzDagtbyW7lWAYr1KHjlRCOUpYZDep7NlF8GPlIi68iUlmP0m7J2N30RTEY
+WQ+wkcE4JX26l09uaRaGkvtRCqGWQnev/EpiU/iCBUNM69Gibkq/7A==
+-----END RSA PRIVATE KEY-----

+ 9 - 0
CMS/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA478XskPJlQaL216X0N/X
+kG23OKmPDJkorDVb7V4kLF48+zoo8SAZMipNPiQo4nMXMjERk8YYrgZzzCESfESi
+EnWtMpqCFKShTWvqDDBqxoS/61xvRmAVSTfgNiGTJSAgnStZ5qJfLFYjf10wy2N2
+rL1PT8K0mg48U50teiUApdOlM9LxKvCscpwKSehvDcrM3gcp6QfCzZMPf/carA8l
+L8l0Ql/F+cjtBGIPKWMgHsm+70i0LZjYyJcFJUsbZW+0LTFVA6/JG7lsNDRpmn6m
+9REfJXgbQqNar8KNMNApM34BkzKzO88IWy/Kn3PRR2FPsxUJ3uIJt5yOCUYP2Btd
+gwIDAQAB
+-----END PUBLIC KEY-----