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