From e11ae02208659186ddd920854ec8b8c2bb50f045 Mon Sep 17 00:00:00 2001 From: fajiao <1519100073@qq.com> Date: Fri, 30 Sep 2022 14:37:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cis.Application/AppConfig.json | 4 + Cis.Application/Cb/Common/CbInfo.cs | 44 +++++ Cis.Application/Cb/Entity/CbCamera.cs | 44 +++++ Cis.Application/Cb/Service/CbCameraService.cs | 56 ++++++ Cis.Application/Cm/Common/CmInfo.cs | 44 +++++ Cis.Application/Cm/Entity/CmMarkGroup.cs | 28 +++ .../Cm/Service/CmMarkGroupService.cs | 15 ++ Cis.Application/Startup.cs | 26 +++ Cis.Application/Sys/Common/SysInfo.cs | 54 ++++++ Cis.Application/Sys/Entity/SysDictData.cs | 54 ++++++ Cis.Application/Sys/Entity/SysDictType.cs | 41 +++++ .../Sys/Service/SysDictDataService.cs | 16 ++ .../Sys/Service/SysDictTypeService.cs | 15 ++ Cis.Core/Attribute/NotTableAttribute.cs | 10 ++ Cis.Core/Cache/CacheSetup.cs | 28 +++ Cis.Core/Cache/SqlSugarCache.cs | 51 ++++++ Cis.Core/Const/SqlSugarConst.cs | 14 ++ Cis.Core/CoreConfig.json | 46 +++++ Cis.Core/Entity/EntityBase.cs | 61 +++++++ Cis.Core/Entity/Pagination.cs | 47 ++++++ Cis.Core/Enum/CacheTypeEnum.cs | 19 +++ Cis.Core/Enum/StatusEnum.cs | 19 +++ Cis.Core/Extension/ObjectExtension.cs | 141 ++++++++++++++++ Cis.Core/GlobalUsing.cs | 23 +++ Cis.Core/Option/CacheOptions.cs | 17 ++ Cis.Core/Option/DbConnectionOptions.cs | 20 +++ Cis.Core/Option/SnowIdOptions.cs | 12 ++ Cis.Core/SqlSugar/IEntityFilter.cs | 13 ++ Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs | 15 ++ Cis.Core/SqlSugar/SqlSugarPagedList.cs | 100 +++++++++++ Cis.Core/SqlSugar/SqlSugarRepository.cs | 16 ++ Cis.Core/SqlSugar/SqlSugarSetup.cs | 159 ++++++++++++++++++ Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs | 65 +++++++ Cis.Core/Util/RespParamProvider.cs | 128 ++++++++++++++ Cis.Web.Core/ProjectOptions.cs | 21 +++ 35 files changed, 1466 insertions(+) create mode 100644 Cis.Application/AppConfig.json create mode 100644 Cis.Application/Cb/Common/CbInfo.cs create mode 100644 Cis.Application/Cb/Entity/CbCamera.cs create mode 100644 Cis.Application/Cb/Service/CbCameraService.cs create mode 100644 Cis.Application/Cm/Common/CmInfo.cs create mode 100644 Cis.Application/Cm/Entity/CmMarkGroup.cs create mode 100644 Cis.Application/Cm/Service/CmMarkGroupService.cs create mode 100644 Cis.Application/Startup.cs create mode 100644 Cis.Application/Sys/Common/SysInfo.cs create mode 100644 Cis.Application/Sys/Entity/SysDictData.cs create mode 100644 Cis.Application/Sys/Entity/SysDictType.cs create mode 100644 Cis.Application/Sys/Service/SysDictDataService.cs create mode 100644 Cis.Application/Sys/Service/SysDictTypeService.cs create mode 100644 Cis.Core/Attribute/NotTableAttribute.cs create mode 100644 Cis.Core/Cache/CacheSetup.cs create mode 100644 Cis.Core/Cache/SqlSugarCache.cs create mode 100644 Cis.Core/Const/SqlSugarConst.cs create mode 100644 Cis.Core/CoreConfig.json create mode 100644 Cis.Core/Entity/EntityBase.cs create mode 100644 Cis.Core/Entity/Pagination.cs create mode 100644 Cis.Core/Enum/CacheTypeEnum.cs create mode 100644 Cis.Core/Enum/StatusEnum.cs create mode 100644 Cis.Core/Extension/ObjectExtension.cs create mode 100644 Cis.Core/GlobalUsing.cs create mode 100644 Cis.Core/Option/CacheOptions.cs create mode 100644 Cis.Core/Option/DbConnectionOptions.cs create mode 100644 Cis.Core/Option/SnowIdOptions.cs create mode 100644 Cis.Core/SqlSugar/IEntityFilter.cs create mode 100644 Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs create mode 100644 Cis.Core/SqlSugar/SqlSugarPagedList.cs create mode 100644 Cis.Core/SqlSugar/SqlSugarRepository.cs create mode 100644 Cis.Core/SqlSugar/SqlSugarSetup.cs create mode 100644 Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs create mode 100644 Cis.Core/Util/RespParamProvider.cs create mode 100644 Cis.Web.Core/ProjectOptions.cs diff --git a/Cis.Application/AppConfig.json b/Cis.Application/AppConfig.json new file mode 100644 index 0000000..07b7167 --- /dev/null +++ b/Cis.Application/AppConfig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", + +} \ No newline at end of file diff --git a/Cis.Application/Cb/Common/CbInfo.cs b/Cis.Application/Cb/Common/CbInfo.cs new file mode 100644 index 0000000..f64368f --- /dev/null +++ b/Cis.Application/Cb/Common/CbInfo.cs @@ -0,0 +1,44 @@ +namespace Cis.Application.Cb; + +/// +/// Cb -> Camera base +/// +public class CbInfo +{ + #region Api Info + + /// + /// Api 分组名 + /// + public const string GroupName = "CameraBase"; + + /// + /// Api 分组排序 + /// + public const int GroupOrder = 100; + + #endregion Api Info + + #region Database Info + + /// + /// 数据库名 + /// + public const string DbName = SqlSugarConst.DefaultConfigId; + + #endregion Database Info + + #region Table Info + + /// + /// CbCamera 表名 + /// + public const string CbCameraTbName = "cb_camera"; + + /// + /// CbCamera 表描述 + /// + public const string CbCameraTbDesc = "相机表"; + + #endregion Table Info +} \ No newline at end of file diff --git a/Cis.Application/Cb/Entity/CbCamera.cs b/Cis.Application/Cb/Entity/CbCamera.cs new file mode 100644 index 0000000..d4a1947 --- /dev/null +++ b/Cis.Application/Cb/Entity/CbCamera.cs @@ -0,0 +1,44 @@ +namespace Cis.Application.Cb; + +/// +/// 系统字典类型表 +/// +[SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)] +[Tenant(CbInfo.DbName)] +public class CbCamera : EntityBase +{ + /// + /// 名称 + /// + [SugarColumn(ColumnDescription = "名称", Length = 64)] + [Required, MaxLength(64)] + public string Name { get; set; } + + /// + /// ip 地址 + /// + [SugarColumn(ColumnDescription = "ip地址", Length = 16)] + [Required, MaxLength(16)] + public string Ip { get; set; } + + /// + /// 端口 + /// + [SugarColumn(ColumnDescription = "端口", Length = 5, DefaultValue = "80")] + [Required, MaxLength(5)] + public string Port { get; set; } + + /// + /// 账号 + /// + [SugarColumn(ColumnDescription = "账号", Length = 32)] + [Required, MaxLength(32)] + public string UserName { get; set; } + + /// + /// 密码 + /// + [SugarColumn(ColumnDescription = "密码", Length = 32)] + [Required, MaxLength(32)] + public string Password { get; set; } +} \ No newline at end of file diff --git a/Cis.Application/Cb/Service/CbCameraService.cs b/Cis.Application/Cb/Service/CbCameraService.cs new file mode 100644 index 0000000..165cffb --- /dev/null +++ b/Cis.Application/Cb/Service/CbCameraService.cs @@ -0,0 +1,56 @@ +using Newtonsoft.Json.Linq; +using System.Runtime.InteropServices; + +namespace Cis.Application.Cb; + +/// +/// 相机服务 +/// +[ApiDescriptionSettings(CbInfo.GroupName, Order = CbInfo.GroupOrder)] +public class CbCameraService : IDynamicApiController, ITransient +{ + private readonly SqlSugarRepository _cbCameraRep; + + public CbCameraService(SqlSugarRepository cbCameraRep) + { + _cbCameraRep = cbCameraRep; + } + + public async Task Get(long id) + { + CbCamera entity = await _cbCameraRep.GetByIdAsync(id); + return entity; + } + + public async Task> GetList(string queryJson) + { + JObject queryObj = queryJson.ToJObject(); + List list = await _cbCameraRep.AsQueryable() + .ToListAsync(); + return list; + } + + public async Task> GetPageList(string queryJson, string pagination) + { + Pagination pageObj = pagination.ToObject(); + JObject queryObj = queryJson.ToJObject(); + List list = await _cbCameraRep.AsQueryable() + .ToPageListAsync(pageObj.Index, pageObj.Size); + return list; + } + + public async Task Add(CbCamera entity) + { + await _cbCameraRep.InsertAsync(entity); + } + + public async Task Update(CbCamera entity) + { + await _cbCameraRep.UpdateAsync(entity); + } + + public async Task Delete(CbCamera entity) + { + await _cbCameraRep.DeleteAsync(entity); + } +} \ No newline at end of file diff --git a/Cis.Application/Cm/Common/CmInfo.cs b/Cis.Application/Cm/Common/CmInfo.cs new file mode 100644 index 0000000..f82d8e1 --- /dev/null +++ b/Cis.Application/Cm/Common/CmInfo.cs @@ -0,0 +1,44 @@ +namespace Cis.Application.Cm; + +/// +/// Cm -> Camera mark +/// +public class CmInfo +{ + #region Api Info + + /// + /// Api 分组名 + /// + public const string GroupName = "CameraMark"; + + /// + /// Api 分组排序 + /// + public const int GroupOrder = 100; + + #endregion Api Info + + #region Database Info + + /// + /// 数据库名 + /// + public const string DbName = SqlSugarConst.DefaultConfigId; + + #endregion Database Info + + #region Table Info + + /// + /// CmMarkGroup 表名 + /// + public const string CmMarkGroupTbName = "cm_mark_group"; + + /// + /// CmMarkGroup 表描述 + /// + public const string CmMarkGroupTbDesc = "标记分组表"; + + #endregion Table Info +} \ No newline at end of file diff --git a/Cis.Application/Cm/Entity/CmMarkGroup.cs b/Cis.Application/Cm/Entity/CmMarkGroup.cs new file mode 100644 index 0000000..b19e0ed --- /dev/null +++ b/Cis.Application/Cm/Entity/CmMarkGroup.cs @@ -0,0 +1,28 @@ +namespace Cis.Application.Cm; + +/// +/// 标记分组表 +/// +[SugarTable(CmInfo.CmMarkGroupTbName, CmInfo.CmMarkGroupTbDesc)] +[Tenant(CmInfo.DbName)] +public class CmMarkGroup : EntityBase +{ + /// + /// 名称 + /// + [SugarColumn(ColumnDescription = "名称", Length = 64)] + public string Name { get; set; } + + /// + /// 排序 + /// + [SugarColumn(ColumnDescription = "排序")] + public int Order { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnDescription = "备注", Length = 256)] + [MaxLength(256)] + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Cis.Application/Cm/Service/CmMarkGroupService.cs b/Cis.Application/Cm/Service/CmMarkGroupService.cs new file mode 100644 index 0000000..faabdb0 --- /dev/null +++ b/Cis.Application/Cm/Service/CmMarkGroupService.cs @@ -0,0 +1,15 @@ +namespace Cis.Application.Cm; + +/// +/// 标记分组服务 +/// +[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] +public class CmMarkGroupService : IDynamicApiController, ITransient +{ + private readonly SqlSugarRepository _cmMarkGroupRep; + + public CmMarkGroupService(SqlSugarRepository cmMarkGroupRep) + { + _cmMarkGroupRep = cmMarkGroupRep; + } +} \ No newline at end of file diff --git a/Cis.Application/Startup.cs b/Cis.Application/Startup.cs new file mode 100644 index 0000000..d88de9c --- /dev/null +++ b/Cis.Application/Startup.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Cis.Application; + +[AppStartup(100)] +public class Startup : AppStartup +{ + /// + /// 配置应用所需服务,在该方法中可以添加应用所需要的功能或服务 + /// + /// + public void ConfigureServices(IServiceCollection services) + { + } + + /// + /// 配置应用请求处理管道 + /// + /// + /// + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + } +} \ No newline at end of file diff --git a/Cis.Application/Sys/Common/SysInfo.cs b/Cis.Application/Sys/Common/SysInfo.cs new file mode 100644 index 0000000..3ffb68d --- /dev/null +++ b/Cis.Application/Sys/Common/SysInfo.cs @@ -0,0 +1,54 @@ +namespace Cis.Application.Sys; + +/// +/// Sys -> System +/// +public class SysInfo +{ + #region Api Info + + /// + /// Api 分组名 + /// + public const string GroupName = "Sys"; + + /// + /// Api 分组排序 + /// + public const int GroupOrder = 100; + + #endregion Api Info + + #region Database Info + + /// + /// 数据库名 + /// + public const string DbName = SqlSugarConst.DefaultConfigId; + + #endregion Database Info + + #region Table Info + + /// + /// SysDictType 表名 + /// + public const string SysDictTypeTbName = "sys_dict_type"; + + /// + /// SysDictType 表描述 + /// + public const string SysDictTypeTbDesc = "系统字典类型表"; + + /// + /// SysDataType 表名 + /// + public const string SysDictDataTbName = "sys_data_data"; + + /// + /// SysDataType 表描述 + /// + public const string SysDictDataTbDesc = "系统字典值表"; + + #endregion Table Info +} \ No newline at end of file diff --git a/Cis.Application/Sys/Entity/SysDictData.cs b/Cis.Application/Sys/Entity/SysDictData.cs new file mode 100644 index 0000000..744e2bf --- /dev/null +++ b/Cis.Application/Sys/Entity/SysDictData.cs @@ -0,0 +1,54 @@ +namespace Cis.Application.Sys; + +/// +/// 系统字典值表 +/// +[SugarTable("sys_dict_data", "系统字典值表")] +public class SysDictData : EntityBase +{ + /// + /// 字典类型Id + /// + [SugarColumn(ColumnDescription = "字典类型Id")] + public long DictTypeId { get; set; } + + /// + /// 字典类型 + /// + [SugarColumn(IsIgnore = true)] + [Navigate(NavigateType.OneToOne, nameof(DictTypeId))] + public SysDictType DictType { get; set; } + + /// + /// 值 + /// + [SugarColumn(ColumnDescription = "值", Length = 128)] + [Required, MaxLength(128)] + public string Value { get; set; } + + /// + /// 编码 + /// + [SugarColumn(ColumnDescription = "编码", Length = 64)] + [Required, MaxLength(64)] + public string Code { get; set; } + + /// + /// 排序 + /// + [SugarColumn(ColumnDescription = "排序")] + public int Order { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnDescription = "备注", Length = 128)] + [MaxLength(128)] + public string Remark { get; set; } + + /// + /// 状态 + /// + [SugarColumn(ColumnDescription = "状态")] + public StatusEnum Status { get; set; } = StatusEnum.Enable; +} \ No newline at end of file diff --git a/Cis.Application/Sys/Entity/SysDictType.cs b/Cis.Application/Sys/Entity/SysDictType.cs new file mode 100644 index 0000000..5c325fc --- /dev/null +++ b/Cis.Application/Sys/Entity/SysDictType.cs @@ -0,0 +1,41 @@ +namespace Cis.Application.Sys; + +/// +/// 系统字典类型表 +/// +[SugarTable(SysInfo.SysDictTypeTbName, SysInfo.SysDictTypeTbDesc)] +public class SysDictType : EntityBase +{ + /// + /// 名称 + /// + [SugarColumn(ColumnDescription = "名称", Length = 64)] + [Required, MaxLength(64)] + public string Name { get; set; } + + /// + /// 编码 + /// + [SugarColumn(ColumnDescription = "编码", Length = 64)] + [Required, MaxLength(64)] + public string Code { get; set; } + + /// + /// 排序 + /// + [SugarColumn(ColumnDescription = "排序")] + public int Order { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnDescription = "备注", Length = 256)] + [MaxLength(256)] + public string Remark { get; set; } + + /// + /// 状态 + /// + [SugarColumn(ColumnDescription = "状态")] + public StatusEnum Status { get; set; } = StatusEnum.Enable; +} \ No newline at end of file diff --git a/Cis.Application/Sys/Service/SysDictDataService.cs b/Cis.Application/Sys/Service/SysDictDataService.cs new file mode 100644 index 0000000..0ac8c68 --- /dev/null +++ b/Cis.Application/Sys/Service/SysDictDataService.cs @@ -0,0 +1,16 @@ +namespace Cis.Application.Sys; + +/// +/// 系统字典值服务 +/// +[ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)] +public class SysDictDataService : IDynamicApiController, ITransient +{ + private readonly SqlSugarRepository _sysDictDataRep; + + public SysDictDataService(SqlSugarRepository sysDictDataRep) + { + _sysDictDataRep = sysDictDataRep; + } + +} \ No newline at end of file diff --git a/Cis.Application/Sys/Service/SysDictTypeService.cs b/Cis.Application/Sys/Service/SysDictTypeService.cs new file mode 100644 index 0000000..74c1b17 --- /dev/null +++ b/Cis.Application/Sys/Service/SysDictTypeService.cs @@ -0,0 +1,15 @@ +namespace Cis.Application.Sys; + +/// +/// 系统字典类型服务 +/// +[ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)] +public class SysDictTypeService : IDynamicApiController, ITransient +{ + private readonly SqlSugarRepository _sysDictTypeRep; + + public SysDictTypeService(SqlSugarRepository sysDictTypeRep) + { + _sysDictTypeRep = sysDictTypeRep; + } +} \ No newline at end of file diff --git a/Cis.Core/Attribute/NotTableAttribute.cs b/Cis.Core/Attribute/NotTableAttribute.cs new file mode 100644 index 0000000..f41f16e --- /dev/null +++ b/Cis.Core/Attribute/NotTableAttribute.cs @@ -0,0 +1,10 @@ +namespace Cis.Core; + +/// +/// 非实体表特性 +/// +[SuppressSniffer] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] +public class NotTableAttribute : Attribute +{ +} \ No newline at end of file diff --git a/Cis.Core/Cache/CacheSetup.cs b/Cis.Core/Cache/CacheSetup.cs new file mode 100644 index 0000000..311cbc0 --- /dev/null +++ b/Cis.Core/Cache/CacheSetup.cs @@ -0,0 +1,28 @@ +using NewLife.Caching; + +namespace Cis.Core; + +public static class CacheSetup +{ + /// + /// 缓存注册(新生命Redis组件) + /// + /// + public static void AddCache(this IServiceCollection services) + { + services.AddSingleton(options => + { + var cacheOptions = App.GetOptions(); + if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString()) + { + var redis = new Redis(); + redis.Init(cacheOptions.RedisConnectionString); + return redis; + } + else + { + return Cache.Default; + } + }); + } +} \ No newline at end of file diff --git a/Cis.Core/Cache/SqlSugarCache.cs b/Cis.Core/Cache/SqlSugarCache.cs new file mode 100644 index 0000000..24e6da3 --- /dev/null +++ b/Cis.Core/Cache/SqlSugarCache.cs @@ -0,0 +1,51 @@ +using NewLife.Caching; + +namespace Cis.Core; + +/// +/// SqlSugar二级缓存(必须是内存缓存) +/// +public class SqlSugarCache : ICacheService, ISingleton +{ + private static readonly ICache _cache = NewLife.Caching.Cache.Default; + + public void Add(string key, V value) + { + _cache.Set(key, value); + } + + public void Add(string key, V value, int cacheDurationInSeconds) + { + _cache.Set(key, value, cacheDurationInSeconds); + } + + public bool ContainsKey(string key) + { + return _cache.ContainsKey(key); + } + + public V Get(string key) + { + return _cache.Get(key); + } + + public IEnumerable GetAllKey() + { + return _cache.Keys; + } + + public V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue) + { + if (!_cache.TryGetValue(cacheKey, out V value)) + { + value = create(); + _cache.Set(cacheKey, value, cacheDurationInSeconds); + } + return value; + } + + public void Remove(string key) + { + _cache.Remove(key); + } +} \ No newline at end of file diff --git a/Cis.Core/Const/SqlSugarConst.cs b/Cis.Core/Const/SqlSugarConst.cs new file mode 100644 index 0000000..814797c --- /dev/null +++ b/Cis.Core/Const/SqlSugarConst.cs @@ -0,0 +1,14 @@ +namespace Cis.Core; + +public class SqlSugarConst +{ + /// + /// 默认数据库标识 + /// + public const string DefaultConfigId = "Cis"; + + /// + /// 默认表主键 + /// + public const string DefaultPrimaryKey = "Id"; +} \ No newline at end of file diff --git a/Cis.Core/CoreConfig.json b/Cis.Core/CoreConfig.json new file mode 100644 index 0000000..b6deb2e --- /dev/null +++ b/Cis.Core/CoreConfig.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", + "DbConnection": { + "ConnectionConfigs": [ + { + "ConfigId": "Cis", + "DbType": "MySql", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access + "ConnectionString": "Data Source=127.0.0.1;port=3306;User ID=root;Password=123456;Database=Cis;pooling=true;sslmode=none;CharSet=utf8;", + //"DbType": "Sqlite", + //"ConnectionString": "DataSource=./cis.db", + //"DbType": "PostgreSQL", + //"ConnectionString": "HOST=127.0.0.1;PORT=5432;USER ID=pgsql;PASSWORD=123456;DATABASE=cis;", + "EnableInitDb": false, // 启用库表初始化 + } + ] + }, + "Cache": { + "CacheType": "Redis", // Memory、Redis + "RedisConnectionString": "127.0.0.1:6379;password=123456;db=2" + }, + "SnowId": { + "WorkerId": 5 // 取值范围0~63,默认1 + }, + "SpecificationDocumentSettings": { + "DocumentTitle": "Swagger",//默认标题 + "DefaultGroupName": "Default"//默认分组名称 + }, + "DynamicApiControllerSettings": { + "DefaultRoutePrefix": "api", //默认路由前缀 + "KeepVerb": true, //是否保留动作谓词 + "KeepName": true, //是否保留默认名称 + "LowercaseRoute": false, //小写路由格式 + "AsLowerCamelCase": true, //启用小驼峰命名(首字母小写) + "UrlParameterization": true // 方法参数 + }, + "AppSettings": { + "InjectSpecificationDocument": true // 生产环境是否开启Swagger + }, + "CorsAccessorSettings": { + "WithExposedHeaders": [ + "access-token", + "x-access-token", + "environment" + ] + } +} diff --git a/Cis.Core/Entity/EntityBase.cs b/Cis.Core/Entity/EntityBase.cs new file mode 100644 index 0000000..af01d77 --- /dev/null +++ b/Cis.Core/Entity/EntityBase.cs @@ -0,0 +1,61 @@ +namespace Cis.Core; + +/// +/// 框架实体基类Id +/// +public abstract class EntityBaseId +{ + /// + /// 雪花Id + /// + [SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true, IsIdentity = false)] + public virtual long Id { get; set; } +} + +/// +/// 框架实体基类 +/// +public abstract class EntityBase : EntityBaseId +{ + /// + /// 创建时间 + /// + [SugarColumn(ColumnDescription = "创建时间")] + public virtual DateTime? CreateTime { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnDescription = "更新时间")] + public virtual DateTime? UpdateTime { get; set; } + + /// + /// 创建者Id + /// + [SugarColumn(ColumnDescription = "创建者Id")] + public virtual long? CreateUserId { get; set; } + + /// + /// 修改者Id + /// + [SugarColumn(ColumnDescription = "修改者Id")] + public virtual long? UpdateUserId { get; set; } + + /// + /// 软删除 + /// + [SugarColumn(ColumnDescription = "软删除")] + public virtual bool IsDelete { get; set; } = false; +} + +/// +/// 业务数据实体基类(数据权限) +/// +public abstract class DataEntityBase : EntityBase +{ + /// + /// 创建者部门Id + /// + [SugarColumn(ColumnDescription = "创建者部门Id")] + public virtual long? CreateOrgId { get; set; } +} \ No newline at end of file diff --git a/Cis.Core/Entity/Pagination.cs b/Cis.Core/Entity/Pagination.cs new file mode 100644 index 0000000..504c41e --- /dev/null +++ b/Cis.Core/Entity/Pagination.cs @@ -0,0 +1,47 @@ +namespace Cis.Core; + +public class Pagination +{ + /// + /// 当前页 + /// + public virtual int Index { get; set; } + + /// + /// 页码容量 + /// + public virtual int Size { get; set; } + + /// + /// 排序字段 + /// + public virtual string Field { get; set; } + + /// + /// 排序方向 + /// + public virtual string Order { get; set; } + + /// + /// 总记录数 + /// + public virtual int Total { get; set; } + + /// + /// 页码数 + /// + public virtual int Number + { + get + { + if (Size > 0) + { + return ((Size % Total) == 0) ? (Size / Total) : ((Size / Total) + 1); + } + else + { + return 0; + } + } + } +} \ No newline at end of file diff --git a/Cis.Core/Enum/CacheTypeEnum.cs b/Cis.Core/Enum/CacheTypeEnum.cs new file mode 100644 index 0000000..0848f8a --- /dev/null +++ b/Cis.Core/Enum/CacheTypeEnum.cs @@ -0,0 +1,19 @@ +namespace Cis.Core; + +/// +/// 缓存类型枚举 +/// +public enum CacheTypeEnum +{ + /// + /// 内存缓存 + /// + [Description("内存缓存")] + Memory, + + /// + /// Redis缓存 + /// + [Description("Redis缓存")] + Redis +} \ No newline at end of file diff --git a/Cis.Core/Enum/StatusEnum.cs b/Cis.Core/Enum/StatusEnum.cs new file mode 100644 index 0000000..27cf060 --- /dev/null +++ b/Cis.Core/Enum/StatusEnum.cs @@ -0,0 +1,19 @@ +namespace Cis.Core; + +/// +/// 通用状态枚举 +/// +public enum StatusEnum +{ + /// + /// 停用 + /// + [Description("停用")] + Disable = 0, + + /// + /// 启用 + /// + [Description("启用")] + Enable = 1, +} \ No newline at end of file diff --git a/Cis.Core/Extension/ObjectExtension.cs b/Cis.Core/Extension/ObjectExtension.cs new file mode 100644 index 0000000..a080cf4 --- /dev/null +++ b/Cis.Core/Extension/ObjectExtension.cs @@ -0,0 +1,141 @@ +using Newtonsoft.Json.Linq; +using System.Text; + +namespace Cis.Core; + +/// +/// 对象拓展 +/// +[SuppressSniffer] +public static class ObjectExtension +{ + /// + /// 判断类型是否实现某个泛型 + /// + /// 类型 + /// 泛型类型 + /// bool + public static bool HasImplementedRawGeneric(this Type type, Type generic) + { + // 检查接口类型 + var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); + if (isTheRawGenericType) return true; + + // 检查类型 + while (type != null && type != typeof(object)) + { + isTheRawGenericType = IsTheRawGenericType(type); + if (isTheRawGenericType) return true; + type = type.BaseType; + } + + return false; + + // 判断逻辑 + bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); + } + + /// + /// 将字典转化为QueryString格式 + /// + /// + /// + /// + public static string ToQueryString(this Dictionary dict, bool urlEncode = true) + { + return string.Join("&", dict.Select(p => $"{(urlEncode ? p.Key?.UrlEncode() : "")}={(urlEncode ? p.Value?.UrlEncode() : "")}")); + } + + /// + /// 将字符串URL编码 + /// + /// + /// + public static string UrlEncode(this string str) + { + return string.IsNullOrEmpty(str) ? "" : System.Web.HttpUtility.UrlEncode(str, Encoding.UTF8); + } + + /// + /// List转DataTable + /// + /// + /// + /// + public static DataTable ToDataTable(this List list) + { + DataTable result = new(); + if (list.Count > 0) + { + // result.TableName = list[0].GetType().Name; // 表名赋值 + PropertyInfo[] propertys = list[0].GetType().GetProperties(); + foreach (PropertyInfo pi in propertys) + { + Type colType = pi.PropertyType; + if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + colType = colType.GetGenericArguments()[0]; + } + if (IsIgnoreColumn(pi)) + continue; + result.Columns.Add(pi.Name, colType); + } + for (int i = 0; i < list.Count; i++) + { + ArrayList tempList = new(); + foreach (PropertyInfo pi in propertys) + { + if (IsIgnoreColumn(pi)) + continue; + object obj = pi.GetValue(list[i], null); + tempList.Add(obj); + } + object[] array = tempList.ToArray(); + result.LoadDataRow(array, true); + } + } + return result; + } + + /// + /// 对象序列化成Json字符串 + /// + /// + /// + public static string ToJson(this object obj) + { + return JSON.GetJsonSerializer().Serialize(obj); + } + + /// + /// Json字符串反序列化成对象 + /// + /// + /// + /// + public static T ToObject(this string json) + { + return JSON.GetJsonSerializer().Deserialize(json); + } + + /// + /// 字串反序列化成linq对象 + /// + /// 字串 + /// + public static JObject ToJObject(this string Json) + { + return Json == null ? JObject.Parse("{}") : JObject.Parse(Json.Replace(" ", "")); + } + + /// + /// 排除SqlSugar忽略的列 + /// + /// + /// + private static bool IsIgnoreColumn(PropertyInfo pi) + { + var sc = pi.GetCustomAttributes(false).FirstOrDefault(u => u.IsIgnore == true); + return sc != null; + } +} \ No newline at end of file diff --git a/Cis.Core/GlobalUsing.cs b/Cis.Core/GlobalUsing.cs new file mode 100644 index 0000000..399597f --- /dev/null +++ b/Cis.Core/GlobalUsing.cs @@ -0,0 +1,23 @@ +global using Furion; +global using Furion.ConfigurableOptions; +global using Furion.DatabaseAccessor; +global using Furion.DataValidation; +global using Furion.DependencyInjection; +global using Furion.FriendlyException; +global using Furion.JsonSerialization; +global using Furion.UnifyResult; +global using Mapster; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.Extensions.DependencyInjection; +global using SqlSugar; +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using System.Data; +global using System.Linq; +global using System.Reflection; +global using System.Threading.Tasks; \ No newline at end of file diff --git a/Cis.Core/Option/CacheOptions.cs b/Cis.Core/Option/CacheOptions.cs new file mode 100644 index 0000000..ace990a --- /dev/null +++ b/Cis.Core/Option/CacheOptions.cs @@ -0,0 +1,17 @@ +namespace Cis.Core; + +/// +/// 缓存配置选项 +/// +public sealed class CacheOptions : IConfigurableOptions +{ + /// + /// 缓存类型 + /// + public string CacheType { get; set; } + + /// + /// Redis连接字符串 + /// + public string RedisConnectionString { get; set; } +} \ No newline at end of file diff --git a/Cis.Core/Option/DbConnectionOptions.cs b/Cis.Core/Option/DbConnectionOptions.cs new file mode 100644 index 0000000..a5e81fc --- /dev/null +++ b/Cis.Core/Option/DbConnectionOptions.cs @@ -0,0 +1,20 @@ +namespace Cis.Core; + +/// +/// 数据库配置选项 +/// +public sealed class DbConnectionOptions : IConfigurableOptions +{ + /// + /// 数据库集合 + /// + public List ConnectionConfigs { get; set; } +} + +public sealed class DbConnectionConfig : ConnectionConfig +{ + /// + /// 启用库表初始化 + /// + public bool EnableInitDb { get; set; } +} \ No newline at end of file diff --git a/Cis.Core/Option/SnowIdOptions.cs b/Cis.Core/Option/SnowIdOptions.cs new file mode 100644 index 0000000..869a8e4 --- /dev/null +++ b/Cis.Core/Option/SnowIdOptions.cs @@ -0,0 +1,12 @@ +namespace Cis.Core; + +/// +/// 雪花Id配置选项 +/// +public sealed class SnowIdOptions : IConfigurableOptions +{ + /// + /// 机器码 + /// + public ushort WorkerId { get; set; } +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/IEntityFilter.cs b/Cis.Core/SqlSugar/IEntityFilter.cs new file mode 100644 index 0000000..1869bb9 --- /dev/null +++ b/Cis.Core/SqlSugar/IEntityFilter.cs @@ -0,0 +1,13 @@ +namespace Cis.Core; + +/// +/// 自定义实体过滤器接口 +/// +public interface IEntityFilter +{ + /// + /// 实体过滤器 + /// + /// + IEnumerable> AddEntityFilter(); +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs b/Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs new file mode 100644 index 0000000..f703306 --- /dev/null +++ b/Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs @@ -0,0 +1,15 @@ +namespace Cis.Core; + +/// +/// 实体种子数据接口 +/// +/// +public interface ISqlSugarEntitySeedData + where TEntity : class, new() +{ + /// + /// 种子数据 + /// + /// + IEnumerable HasData(); +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/SqlSugarPagedList.cs b/Cis.Core/SqlSugar/SqlSugarPagedList.cs new file mode 100644 index 0000000..577f1cf --- /dev/null +++ b/Cis.Core/SqlSugar/SqlSugarPagedList.cs @@ -0,0 +1,100 @@ +namespace Cis.Core; + +/// +/// 分页泛型集合 +/// +/// +public class SqlSugarPagedList + where TEntity : new() +{ + /// + /// 页码 + /// + public int Page { get; set; } + + /// + /// 页容量 + /// + public int PageSize { get; set; } + + /// + /// 总条数 + /// + public int Total { get; set; } + + /// + /// 总页数 + /// + public int TotalPages { get; set; } + + /// + /// 当前页集合 + /// + public IEnumerable Items { get; set; } + + /// + /// 是否有上一页 + /// + public bool HasPrevPage { get; set; } + + /// + /// 是否有下一页 + /// + public bool HasNextPage { get; set; } +} + +/// +/// 分页拓展类 +/// +public static class SqlSugarPagedExtensions +{ + /// + /// 分页拓展 + /// + /// + /// + /// + /// + public static SqlSugarPagedList ToPagedList(this ISugarQueryable entity, int pageIndex, int pageSize) + where TEntity : new() + { + var total = 0; + var items = entity.ToPageList(pageIndex, pageSize, ref total); + var totalPages = (int)Math.Ceiling(total / (double)pageSize); + return new SqlSugarPagedList + { + Page = pageIndex, + PageSize = pageSize, + Items = items, + Total = total, + TotalPages = totalPages, + HasNextPage = pageIndex < totalPages, + HasPrevPage = pageIndex - 1 > 0 + }; + } + + /// + /// 分页拓展 + /// + /// + /// + /// + /// + public static async Task> ToPagedListAsync(this ISugarQueryable entity, int pageIndex, int pageSize) + where TEntity : new() + { + RefAsync total = 0; + var items = await entity.ToPageListAsync(pageIndex, pageSize, total); + var totalPages = (int)Math.Ceiling(total / (double)pageSize); + return new SqlSugarPagedList + { + Page = pageIndex, + PageSize = pageSize, + Items = items, + Total = total, + TotalPages = totalPages, + HasNextPage = pageIndex < totalPages, + HasPrevPage = pageIndex - 1 > 0 + }; + } +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/SqlSugarRepository.cs b/Cis.Core/SqlSugar/SqlSugarRepository.cs new file mode 100644 index 0000000..c8fe3dc --- /dev/null +++ b/Cis.Core/SqlSugar/SqlSugarRepository.cs @@ -0,0 +1,16 @@ +namespace Cis.Core; + +/// +/// SqlSugar仓储类 +/// +/// +public class SqlSugarRepository : SimpleClient where T : class, new() +{ + protected ITenant iTenant = null; // 多租户事务 + + public SqlSugarRepository(ISqlSugarClient context = null) : base(context) // 默认值等于null不能少 + { + iTenant = App.GetService().AsTenant(); + base.Context = iTenant.GetConnectionWithAttr(); + } +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/SqlSugarSetup.cs b/Cis.Core/SqlSugar/SqlSugarSetup.cs new file mode 100644 index 0000000..f1cafcf --- /dev/null +++ b/Cis.Core/SqlSugar/SqlSugarSetup.cs @@ -0,0 +1,159 @@ +namespace Cis.Core; + +public static class SqlSugarSetup +{ + /// + /// Sqlsugar 上下文初始化 + /// + /// + public static void AddSqlSugar(this IServiceCollection services) + { + var dbOptions = App.GetOptions(); + var configureExternalServices = new ConfigureExternalServices + { + EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required + { + if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + || (type.PropertyType == typeof(string) && type.GetCustomAttribute() == null)) + column.IsNullable = true; + }, + DataInfoCacheService = new SqlSugarCache(), + }; + dbOptions.ConnectionConfigs.ForEach(config => + { + config.ConfigureExternalServices = configureExternalServices; + config.InitKeyType = InitKeyType.Attribute; + config.IsAutoCloseConnection = true; + config.MoreSettings = new ConnMoreSettings + { + IsAutoRemoveDataCache = true + }; + }); + + SqlSugarScope sqlSugar = new(dbOptions.ConnectionConfigs.Adapt>(), client => + { + dbOptions.ConnectionConfigs.ForEach(config => + { + var db = client.GetConnectionScope((string)config.ConfigId); + + // 设置超时时间 + db.Ado.CommandTimeOut = 30; + + // 打印SQL语句 + db.Aop.OnLogExecuting = (sql, pars) => + { + if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) + Console.ForegroundColor = ConsoleColor.Green; + if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase)) + Console.ForegroundColor = ConsoleColor.White; + if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase)) + Console.ForegroundColor = ConsoleColor.Blue; + Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n"); + App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); + }; + db.Aop.OnError = (ex) => + { + Console.ForegroundColor = ConsoleColor.Red; + var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); + Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"); + App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}"); + }; + + // 数据审计 + db.Aop.DataExecuting = (oldValue, entityInfo) => + { + // 新增操作 + if (entityInfo.OperationType == DataFilterType.InsertByObject) + { + // 主键(long类型)且没有值的---赋值雪花Id + if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long)) + { + var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue); + if (id == null || (long)id == 0) + entityInfo.SetValue(Yitter.IdGenerator.YitIdHelper.NextId()); + } + if (entityInfo.PropertyName == "CreateTime") + entityInfo.SetValue(DateTime.Now); + } + // 更新操作 + if (entityInfo.OperationType == DataFilterType.UpdateByObject) + { + if (entityInfo.PropertyName == "UpdateTime") + entityInfo.SetValue(DateTime.Now); + } + }; + }); + }); + + // 初始化数据库表结构及种子数据 + InitDataBase(sqlSugar, dbOptions); + + services.AddSingleton(sqlSugar); // 单例注册 + services.AddScoped(typeof(SqlSugarRepository<>)); // 注册仓储 + services.AddUnitOfWork(); // 注册事务与工作单元 + } + + /// + /// 初始化数据库结构 + /// + private static void InitDataBase(SqlSugarScope db, DbConnectionOptions dbOptions) + { + // 创建数据库 + dbOptions.ConnectionConfigs.ForEach(config => + { + if (!config.EnableInitDb || config.DbType == SqlSugar.DbType.Oracle) return; + db.GetConnectionScope(config.ConfigId).DbMaintenance.CreateDatabase(); + }); + + // 获取所有实体表-初始化表结构 + var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass + && u.IsDefined(typeof(SugarTable), false) && !u.IsDefined(typeof(NotTableAttribute), false)); + if (!entityTypes.Any()) return; + foreach (var entityType in entityTypes) + { + var tAtt = entityType.GetCustomAttribute(); // 多数据库 + var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString(); + if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb) + continue; + var db2 = db.GetConnectionScope(configId); + var splitTable = entityType.GetCustomAttribute(); // 分表 + if (splitTable == null) + db2.CodeFirst.InitTables(entityType); + else + db2.CodeFirst.SplitTables().InitTables(entityType); + } + + // 获取所有种子配置-初始化数据 + var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass + && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)))); + if (!seedDataTypes.Any()) return; + foreach (var seedType in seedDataTypes) + { + var instance = Activator.CreateInstance(seedType); + + var hasDataMethod = seedType.GetMethod("HasData"); + var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); + if (seedData == null) continue; + + var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); + var tAtt = entityType.GetCustomAttribute(); + var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString(); + if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb) + continue; + var db2 = db.GetConnectionScope(configId); + var seedDataTable = seedData.ToList().ToDataTable(); + seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName; + if (seedDataTable.Columns.Contains(SqlSugarConst.DefaultPrimaryKey)) + { + var storage = db2.Storageable(seedDataTable).WhereColumns(SqlSugarConst.DefaultPrimaryKey).ToStorage(); + storage.AsInsertable.ExecuteCommand(); + storage.AsUpdateable.ExecuteCommand(); + } + else // 没有主键或者不是预定义的主键(没主键有重复的可能) + { + var storage = db2.Storageable(seedDataTable).ToStorage(); + storage.AsInsertable.ExecuteCommand(); + } + } + } +} \ No newline at end of file diff --git a/Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs b/Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs new file mode 100644 index 0000000..45b1f9b --- /dev/null +++ b/Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs @@ -0,0 +1,65 @@ +namespace Cis.Core; + +/// +/// SqlSugar 事务和工作单元 +/// +public sealed class SqlSugarUnitOfWork : IUnitOfWork +{ + /// + /// SqlSugar 对象 + /// + private readonly ISqlSugarClient _sqlSugarClient; + + /// + /// 构造函数 + /// + /// + public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient) + { + _sqlSugarClient = sqlSugarClient; + } + + /// + /// 开启工作单元处理 + /// + /// + /// + /// + public void BeginTransaction(FilterContext context, UnitOfWorkAttribute unitOfWork) + { + _sqlSugarClient.AsTenant().BeginTran(); + } + + /// + /// 提交工作单元处理 + /// + /// + /// + /// + public void CommitTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork) + { + _sqlSugarClient.AsTenant().CommitTran(); + } + + /// + /// 回滚工作单元处理 + /// + /// + /// + /// + public void RollbackTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork) + { + _sqlSugarClient.AsTenant().RollbackTran(); + } + + /// + /// 执行完毕(无论成功失败) + /// + /// + /// + /// + public void OnCompleted(FilterContext context, FilterContext resultContext) + { + _sqlSugarClient.Dispose(); + } +} \ No newline at end of file diff --git a/Cis.Core/Util/RespParamProvider.cs b/Cis.Core/Util/RespParamProvider.cs new file mode 100644 index 0000000..fcd6a89 --- /dev/null +++ b/Cis.Core/Util/RespParamProvider.cs @@ -0,0 +1,128 @@ +namespace Cis.Core; + +/// +/// 全局规范化结果 +/// +[UnifyModel(typeof(RespParam<>))] +public class RespParamProvider : IUnifyResultProvider +{ + /// + /// 异常返回值 + /// + /// + /// + /// + public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata) + { + return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors)); + } + + /// + /// 成功返回值 + /// + /// + /// + /// + public IActionResult OnSucceeded(ActionExecutedContext context, object data) + { + return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data)); + } + + /// + /// 验证失败返回值 + /// + /// + /// + /// + public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata) + { + return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult)); + } + + /// + /// 特定状态码返回值 + /// + /// + /// + /// + /// + public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings) + { + // 设置响应状态码 + UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings); + + switch (statusCode) + { + // 处理 401 状态码 + case StatusCodes.Status401Unauthorized: + await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 登录已过期,请重新登录"), + App.GetOptions()?.JsonSerializerOptions); + break; + // 处理 403 状态码 + case StatusCodes.Status403Forbidden: + await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 禁止访问,没有权限"), + App.GetOptions()?.JsonSerializerOptions); + break; + + default: break; + } + } + + /// + /// 返回 RESTful 风格结果集 + /// + /// + /// + /// + /// + /// + private static RespParam RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default) + { + return new RespParam + { + Code = statusCode, + Message = errors is string str ? str : JSON.Serialize(errors), + Data = data, + Type = succeeded ? "success" : "error", + Extras = UnifyContext.Take(), + Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; + } +} + +/// +/// 全局返回结果 +/// +/// +public class RespParam +{ + /// + /// 状态码 + /// + public int Code { get; set; } + + /// + /// 类型success、warning、error + /// + public string Type { get; set; } + + /// + /// 错误信息 + /// + public string Message { get; set; } + + /// + /// 数据 + /// + public T Data { get; set; } + + /// + /// 附加数据 + /// + public object Extras { get; set; } + + /// + /// 时间戳 + /// + public long Timestamp { get; set; } +} \ No newline at end of file diff --git a/Cis.Web.Core/ProjectOptions.cs b/Cis.Web.Core/ProjectOptions.cs new file mode 100644 index 0000000..7a9642f --- /dev/null +++ b/Cis.Web.Core/ProjectOptions.cs @@ -0,0 +1,21 @@ +using Cis.Core; +using Microsoft.Extensions.DependencyInjection; + +namespace Cis.Web.Core; + +public static class ProjectOptions +{ + /// + /// 注册项目配置选项 + /// + /// + /// + public static IServiceCollection AddProjectOptions(this IServiceCollection services) + { + services.AddConfigurableOptions(); + services.AddConfigurableOptions(); + services.AddConfigurableOptions(); + + return services; + } +} \ No newline at end of file