diff --git a/.docs/README.md b/.docs/README.md
new file mode 100644
index 00000000..c7aaa452
--- /dev/null
+++ b/.docs/README.md
@@ -0,0 +1,2 @@
+BlogCore官方文档仓库地址已经迁移到:
+https://gitee.com/laozhangIsPhi/Blog.Core.E-Book
\ No newline at end of file
diff --git a/.docs/contents/.vuepress/config.js b/.docs/contents/.vuepress/config.js
deleted file mode 100644
index 6cb23754..00000000
--- a/.docs/contents/.vuepress/config.js
+++ /dev/null
@@ -1,46 +0,0 @@
-module.exports = {
- title: 'Blog.Core',
- description: 'Hello, 欢迎使用前后端分离之 ASP.NET Core 后端全家桶框架!',
- base : '/.doc/',
- head: [
- ['link', {
- rel: 'icon',
- href: `/favicon.ico`
- }]
- ],
- dest: './contents/.vuepress/dist',
- ga: '',
- evergreen: true,
- themeConfig: {
- nav: [
- { text: '首页', link: '/' },
- { text: '指南', link: '/guide/' },
- { text: '更新日志', link: '/Update/' },
- { text: '压测', link: '/PressureTest/' },
- { text: '参与贡献', link: '/Contribution/' },
- { text: 'BCVP社区', link: '/QQ/' },
- { text: '接口API', link: 'http://apk.neters.club' },
- { text: '管理后台', link: 'http://vueadmin.neters.club' },
- { text: 'Github', link: 'https://github.com/anjoy8/Blog.Core' },
- ],
- sidebarDepth: 2,
- sidebar: {
- '/guide/': getGuideSidebar('Guide'),
- }
- }
-}
-
-function getGuideSidebar (groupA) {
- return [
- {
- title: groupA,
- collapsable: false,
- children: [
- '',
- 'getting-started',
- 'function-sheet',
- 'cheat-sheet'
- ]
- }
- ]
- }
\ No newline at end of file
diff --git a/.docs/contents/.vuepress/public/bcvphomelogo.png b/.docs/contents/.vuepress/public/bcvphomelogo.png
deleted file mode 100644
index e1bf0f79..00000000
Binary files a/.docs/contents/.vuepress/public/bcvphomelogo.png and /dev/null differ
diff --git a/.docs/contents/.vuepress/public/favicon.ico b/.docs/contents/.vuepress/public/favicon.ico
deleted file mode 100644
index 68062fe0..00000000
Binary files a/.docs/contents/.vuepress/public/favicon.ico and /dev/null differ
diff --git a/.docs/contents/Contribution/README.md b/.docs/contents/Contribution/README.md
deleted file mode 100644
index 027e74b3..00000000
--- a/.docs/contents/Contribution/README.md
+++ /dev/null
@@ -1,104 +0,0 @@
-# 贡献
-
-
-欢迎一起完善文档,
-参与打赏的小可爱名单如下(单位:元),你们的贡献是我继续的动力:
-(2021年1月14日 10点25分)
-
-
-|序号|微信昵称|助力值|备注|
-|-|-|-|-|
-|01|排 * * 瓜|100||
-|02|船 * * 长|100||
-|03|二 * * 生|1||
-|04||1|未留微信号|
-|05|旭 * * 光|10||
-|06||12.66|未留微信号|
-|07|Ro * * st|10||
-|08|陈 * * 朝|10||
-|09|勇 * * 勇|10||
-|10|袁 * * 嘉|10||
-|11|En * * us|20||
-|12|风 * * 在|18||
-|13|林 * * 杰|10||
-|14|枫 * * 叶|10||
-|15|火 * * 鸟 |50||
-|16|阿 * * 福|10||
-|17||20|未留微信号|
-|18|Er * * or|100|未留微信号|
-|19|陶 * * ve|20||
-|20|熊 * * 育|50||
-|21|点 * * 痕|20||
-|22|夏 * * 目|20||
-|23|CL * * L|50||
-|24|rm * * rf|100||
-|25|Je * * ca|30|搜不到微信号|
-|26|W * * 生|50||
-|27|鹏 * * 郎|20||
-|28|ws * * ai|10||
-|29|逐 * * 梦|20||
-|30|Jo * * aH|10||
-|31|Do * * n|10||
-|32|灰 * * 白|50||
-|33|Ne * * er|100||
-|34|Ar * * as|10||
-|35|吉 * * 祥|36||
-|36|ma * * y|10||
-|37|Yu * * ic|30||
-|38|亡 * * 死|30||
-|39|板 * * 根|20||
-|40|-- * * -|100|未留微信号|
-|41|t * * |20||
-|42|王 * * 聪 |10|未留微信号|
-|43|哈 * * 方|50||
-|44|le * * on|30||
-|45|李 * * |10||
-|46|不 * * 染|10|未留微信号|
-|47|林 * * LIN|10||
-|48|阿 * * 奇|30||
-|49|哒 * * 哒|10||
-|50|王 * * 龙|100||
-|51|Ja * * Tu|100||
-|52|it * * hi|100||
-|53|沙 * * 锋|50|未留微信号|
-|54|Ba * * ai|10||
-|55|古 * * 桐|10||
-|56|小 * * 柜|20||
-|57|rm * * rf|100||
-|58|高 * * 源|10||
-|59|Qq * * 80|10|未留微信号|
-|60|如 * * 寄|20||
-|61|大 * * 灰|10|未留微信号|
-|62|包 * * 华|10|微信号错误|
-|63|西 * * 5°|30||
-|64|王 * * 粮|20||
-|65|Q1 * * ..|30||
-|66|Le * * ry|100||
-|67|bo * * t|30||
-|68|早 * * 蛋|10||
-|69|罗 * * 啊|10||
-|70|40 * * er|30||
-|71|胡 * * 运|30||
-|72|昌 * * 进|5||
-|73|甲 * * 学|15||
-|74|小 * * 走|100||
-|75|啊 * * 辉|50||
-|76|D * * W|100||
-|77|简 * * 简|99||
-|78|空 * * 空|101||
-|79|朱 * * 傻|20||
-|80|Mr * * hi|50||
-|81|王 * * 飞|110||
-|82|Mr * * nn|20|微信号未搜到|
-|83|蓝 * * 兴|30||
-|84|Et * * ng|100||
-|85|Ke * * on|10||
-|86|几 * * 己|20||
-|87|张 * * 群|20||
-|88|睡 * * 鱼|30||
-|89|Mr * * 宋|50||
-|90|布 * * 布|10||
-|91|s * * s|6.6||
-|92|V * * V|50||
-
-
diff --git a/.docs/contents/PressureTest/README.md b/.docs/contents/PressureTest/README.md
deleted file mode 100644
index af2a2e93..00000000
--- a/.docs/contents/PressureTest/README.md
+++ /dev/null
@@ -1,80 +0,0 @@
-# 框架压测报告
-
-
-## 1、测试工具
-使用 `JMeter` 进行压力测试。
-测试时间:2021年1月21日 09点38分。
-服务器报告:
-
-
-
-
-## 2、测试准备
-因为 `JMeter` 是使用 `JAVA` 写的,所以使用 `JMeter` 之前,先安装 `JAVA` 环境。
-安装好后,在 `bin` 文件夹下,点击 `jmeter.bat` 启动程序。
-启动之后会有两个窗口,一个`cmd`窗口,一个`JMeter`的 `GUI`。前面不要忽略`CMD`窗口的提示信息,不要关闭它。
-注意:使用`API`模式,不要使用`GUI`模式。
-
-
-## 3、测试配置
-本地发布后的 `windows` 环境,直接用 `kestrel` 启动。
-线程数:100
-循环数:1000
-HTTP默认值:协议:`http`;服务器或IP:`localhost`;端口号:`9291`;
-HTTP请求:方法:GET;路径:`/api/blog/ApacheTestUpdate`
-HTTP信息请求管理器:无
-响应断言:无
-
-
-
-## 4、项目初始化
-目前采用 `Blog.Core` 默认的配置,
-只开启了内存 `AOP` ,
-其他的都是默认的,然后也把任务调度也关闭了,
-最后注意要把 `IP限流`给关闭,不然压测没效果,因为限流了:
-
-
-
-## 5、压测过程
-
-##### 第一阶段
-
-
-
-
-##### 第二阶段
-
-
-
-
-##### 第三阶段
-
-
-
-
-##### 第四阶段(压测后,检测内存是否降低,20m后)
-
-
-
-##### 第五阶段(停止压测1h后)
-
-
-
-## 6、测试结果
-内存方面,`100*1000` 的 **压测过程中** (写操作),项目保证所占内存在 `350m~500m` 之间
-然后停止一个小时后,内存将为`150m~200m`:
-
-
-
-
-
-
-## 7、压测配置文件下载
- [配置文件](https://img.neters.club/doc/blogcore_blog_ApacheTestUpdate.jmx)
- 下载后,导入到工具里,可以直接测试,察看结果树。
-
-## 8、Docker 镜像
- 已经提交到 `docker hub` 自行拉取操作即可:
- ```
- docker pull laozhangisphi/apkimg:latest
- ```
diff --git a/.docs/contents/QQ/README.md b/.docs/contents/QQ/README.md
deleted file mode 100644
index 31b7f909..00000000
--- a/.docs/contents/QQ/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## 开源社区
-
-
-
-[https://github.com/BaseCoreVueProject/Home](https://github.com/BaseCoreVueProject/Home)
-
-Base netCore (Vue) Project Team,
-基于Net/Core 和Vue(react/ng),快速搭建 MVC & SPA 及微服务应用
-如果你有关于dotNet/core 的,不错的,可以正常运行,且一年内维护的,均可以加入。
-唯一宗旨:我们来自社区,服务社区,反哺社区。
-
-
-
-## 微信公众号
-
-
-
-
diff --git a/.docs/contents/README.md b/.docs/contents/README.md
deleted file mode 100644
index 597f16d9..00000000
--- a/.docs/contents/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-home: true
-heroImage: /bcvphomelogo.png
-actionText: 快速上手 →
-actionLink: /guide/
-features:
-- title: 详尽的文档
- details: 通过详细的文章和视频讲解,将知识点各个击破,入门ASP.Net Core不再难
-- title: 强大的社区
- details: 通过 QQ 群,和数千位同业大佬一起切磋交流。
-- title: 丰富的内容
- details: 框架涵盖ASP.Net Core开发中常见的基本知识点,不仅适合初学者入门,同时也适用于企业级别的开发。
-footer: MIT Licensed | Copyright © 2018-2020-老张的哲学 Powered by VUEPRESS on CentOS 7.6
----
\ No newline at end of file
diff --git a/.docs/contents/Update/README.md b/.docs/contents/Update/README.md
deleted file mode 100644
index 870f98ec..00000000
--- a/.docs/contents/Update/README.md
+++ /dev/null
@@ -1,198 +0,0 @@
-
-## 更新日志
-
-
-### 2021-08-21
-
-重要功能增加:项目增加 `Apollo` 配置中心;
-
-### 2021-08-03
-
-重要功能增加:项目增加 `ES` 搜索,增加 `Serilog` 使用 `tcp` 的方式自定义格式化,写入 `elk` 的实现;
-
-### 2021-06-28
-
-功能增加:项目增加 `nacos` 配置,支持将项目注册到 `nacos` 服务中心,搭建微服务之子服务;
-
-### 2021-06-04
-
-小功能更新:执行的时候,将 `Sql` 日志输出到控制台,方便查看,支持配置文件关闭;
-
-### 2021-05-01
-
-组件更新:多项日志中间件,由自写组件,转为使用`serilog`组件记录日志;
-
-### 2021-03-03
-
-项目目录调整:新增测试文件夹和模板文件夹;
-
-### 2021-02-09
-
-重大项目更新:新增建行聚合支付;
-
-### 2021-01-11
-
-更新:优化任务调度功能,新增暂停和停止;
-
-### 2020-12-02
-
-更新:新增调用`MongoDB`功能,功能可用待完善中;
-
-
-### 2020-11-19
-> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.5.2.nupkg`
-> 主要内容:1、泛型主键;2、通过测试中间件;3、`RabbitMQ`消息队列
-
-
-### 2020-11-18
-
-项目更新:新增`RabbitMQ`消息队列和`EventBus`事件总线,功能可用待完善。
-
-
-### 2020-11-11
-
-项目重大更新:更新至`.NET 5.0`。
-
-
-### 2020-11-05
-
-项目更新:增加`测试用户`中间件,通过一键操作可以跳过权限限制,方便调试,文章[使用测试用户中间件](http://apk.neters.club/api/Blog/GoUrl?id=156)。
-
-
-
-### 2020-10-11
-
-项目更新:设计泛型主键功能,可以在项目初始化的时候设计主键类型。
-
-
-
-### 2020-09-18
-
-项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.2.3.nupkg` 。
-> 1、增加 `Redis` 消息队列功能;
-
-
-
-### 2020-09-04
-
-项目更新:增加 `Redis` 消息队列功能;
-
-
-### 2020-08-06
-
-项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.2.0.nupkg` 。
-> 1、根据解决方案名,来自动导入model;
-> 2、单独封装服务扩展层 `Blog.Core.Extensions` ;
-> 3、代码生成器,支持控制器文件的生成;
-> 4、弱化仓储层,用泛型仓储基类注入服务;
-
-
-
-
-### 2020-08-01
-
-> 重大结构更新:弱化仓储层,通过泛型仓储基类,来实现仓储服务注入,并去掉`Blog.Core.IRepository` 接口层;
-
-### 2020-07-03
-
-> 更新:`DbFirstController` 生成四层文件,目前新增支持 `控制器Controller` 文件的输出;
-
-
-### 2020-06-22
-
-> 项目更新:将服务扩展和自定义中间件,单独封装一层 `Blog.Core.Extensions` ,更解耦。
-
-
-
-### 2020-06-08
-
-> 简单项目更新:生成数据库表结构的时候,利用反射机制,自动生成固定命名空间 `Blog.Core.Model.Models` 下的全部实体.
-> 同时判断表是否存在,如果存在下次不再重复生成。
-
-
-### 2020-06-06
-
-项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.1.0.nupkg` [1a726f8](https://github.com/anjoy8/Blog.Core/commit/1a726f890e527c978982071462e82db4478632f0),更新项目即可 。
-> 1、配置内容展示到控制台;
-> 2、简化封装 `Startup.cs` 类文件;
-> 3、`DbFirst` 模式支持多库模式;
-> 4、`Log4net` 讲异常和 `Info` 分开;
-> 5、修复 `BlogLogAop` 偶尔卡顿问题;
-> 6、将生成种子数据和任务调度功能,封装到中间件;
-> 7、获取当前项目在服务器中的运行信息;
-> 8、删除所有的不需要的 `using` 指令;
-
-
-
-
-### 2020-05-29
-项目启动开启 `QuzrtzNet` 调度任务,并且在 `Admin` 后台管理中配置操作界面;
-> 内容更新:封装生成种子数据的入口方法;
-
-
-
-### 2020-05-12
-修复:支持多库模式下,生成项目模板代码 `DbFirstController` [102c6d6](https://github.com/anjoy8/Blog.Core/commit/102c6d6bfcafd06bf5241844759dea5e7a6815da)
-> 注意:`T4` 模板不能此功能,一次只能一个数据库,且只能 `SqlServer`
-
-
-### 2020-05-07
-> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.1.0.nupkg` [7f64fde](https://github.com/anjoy8/Blog.Core/commit/7f64fde5507f7a8572372dcadb6af5110bd37d68)
-
-
-### 2020-05-06
-> 重大内容更新:优化Log4Net使用方案,完美配合 `NetCore` 官方的 `ILogger`, [ecaffb6](https://github.com/anjoy8/Blog.Core/commit/ecaffb66bdf10a90c087d01e6e817e54f23a97d4)
-
-
-### 2020-05-01
-
-> 重要内容更新:配合Admin全部完成按钮级别权限,更新初始化种子数据
-
-### 2020-04-27
-
-增加功能:配合前端Admin,增加页面 `KeepAlive` 功能;
-增加功能:增加 `Sql` 语句查询Demo,支持返回 `DataTable`;
-
-
-### 2020-04-25
-
-增加功能:`Http api` 接口调用,满足微服务需求
-> 重要内容更新:优化 `Appsettings.app()` 方法,通过官方 `IConfiguration` 接口来获取DBS连接字符串;
-> 优化 `BlogLogAOP.cs`
-
-
-### 2020-04-15
-
-> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.1.11.30.nupkg`
-
-
-### 2020-04-14
-> 重大内容更新:主分支,可以通过配置,一键切换JWT和Ids4认证授权模式
-
-
-### 2020-03-30
-> 重大内容更新:统一所有接口返回格式
-
-
-### 2020-03-25
-增加功能:支持读写分离(目前是三种模式:单库、多库、读写分离)
-> 重大BUG更新:系统登录接口,未对用户软删除进行判断,现已修复
-> API: /api/login/GetJwtToken3
-> Code: await _sysUserInfoServices.Query(d => d.uLoginName == name && d.uLoginPWD == pass && d.tdIsDelete == false);
-
-
-
-### 2020-03-18
-增加功能:创建 Quartz.net 任务调度服务
-
-
-### 2020-01-09
-增加功能:项目迁移到IdentityServer4,统一授权认证中心
-
-
-### 2020-01-05
-增加功能:设计一个简单的中间件,可以查看所有已经注入的服务
-
-
-### 2020-01-04
-增加功能:Ip限流,防止过多刷数据
diff --git a/.docs/contents/guide/README.md b/.docs/contents/guide/README.md
deleted file mode 100644
index 30950e0c..00000000
--- a/.docs/contents/guide/README.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# W 文档指南
-## 亮点与优势
-
-Blog.Core 是一个开箱即用的企业级权限管理应用框架。
-采用最新的前后端完全分离技术【 ASP.NET Core Api 5.0 + Vue 2.x 】。
-并结合 `IdentityServer4` ,可快速解决多客户端和多资源服务的统一认证与鉴权的问题。
-
-## 其他资料
-
-博客园,早期架构搭建:[博客园](https://www.cnblogs.com/laozhang-is-phi/p/9495618.html)
-公众号,后期调整:[文章](https://mvp.neters.club/MVP_aspnetcore_2020/2020)
-视频:[B站](https://www.bilibili.com/video/BV1vC4y1p7Za)
-
-
-## 配套站点
-
-本资源服务器,配合多个项目,构建前后端权限一体化平台,前端用 `VUE` 框架。
-前端-客户端:[预览](https://vueadmin.neters.club/)、[源码](https://github.com/anjoy8/Blog.Admin)
-前端-管理后台:[预览](http://vueblog.neters.club/)、[源码](https://github.com/anjoy8/Blog.Vue)
-认证平台:[预览](https://ids.neters.club/)、[源码](https://github.com/anjoy8/Blog.IdentityServer)
-
-
-### 为什么选择 ASPNET.Core
-1、【开源】`ASPNET.NET Core` 是由 `Microsoft` 和 `.NET` 社区在 `GitHub` 上开源并维护的一个跨平台(支持 Windows、macOS 和 Linux)的新一代高性能框架,
-拥有十分广泛的社区与支持者,可用于构建web应用、物联网IOT应用和移动端应用。
-2、【高效】Asp.net core(.net core)来源于.net,很容易迁移,而且也很容易上手,
-但是又是不同的一个框架,除了上述对.net开发者十分友好以外,相对于之前的.net项目,速度上有巨大的改进,
-相比与原来的`Web(.net framework 4.6)`程序性能提升了`2300%`。跟`python`、`java`等相同环境比较,性能都要优越,
-参考[www.techempower.com](https://www.techempower.com/benchmarks/)。
-3、【跨平台】可以在`Windows`、`Mac`和`Linux`构建和运行跨平台的`Asp.Net Core`应用。
-4、【云原生】在云原生领域拥有天然的优势,搭配Azure云服务,配合K8s,更好的实现分布式应用,以及微服务应用。
-5、【微服务】`ASP.NET Core`尤其适用于微服务架构,也就是说ASP.NET Core不仅适合于中小型项目而且还特别适合于大型,超大型项目。
-6、【大公司】目前国内采用`ASP.NET Core`的大公司比如腾讯、网易,国际的有Bing,GoDaddy,Stackoverflow,Adobe,Microsoft
-7、【总结来说】,`java`支持的,`ASPNET.Core`都支持,而且更轻量级、更高效跨,并且对.net开发者十分友好,微服务案例成熟。
-
-
-
-### 框架功能点
-1、丰富完整的接口文档,在查看的基础上,可以模拟前端调用,更方便。
-2、采用多层开发,隔离性更好,封装更完善。
-3、基于项目模板,可以一键创建自己的项目。
-4、搭配代码生成器,实现快速开发,节省成本。
-5、项目集成多库模式以及读写分离模式,可以同时处理多个数据库的不同模块,更快更安全。
-6、集成统一认证平台 `IdentityServer4` ,实现多个项目的统一认证管理,解决了之前一个项目,
-一套用户的弊端,更适用微服务的开发。
-7、丰富的审计日志处理,方便线上项目快速定位异常点。
-8、支持自由切换多种数据库,Sqlite/SqlServer/MySql/PostgreSQL/Oracle;
-9、支持 `Docker` 容器化开发,可以搭配 k8s 更好的实现微服务。
-
-
-### 应用领域
-1、【对接第三方api】项目通过`webapi`,可以快速对接第三方`api`服务,实现业务逻辑。
-2、【前后端分离】 采用的是`API`+前端的完全分离的开发模式,满足平时开发的所有需求,
-你可以对接任何的自定义前端项目:无论是微信小程序,还是授权APP,无论是PC网页,
-还是手机H5。
-3、【多项目】同时框架还集成了一套鉴权平台,采用IdentityServer4,可以快速的实现多个客户端的认证与授权服务,
-从而大大的减少了平时的工作量,可以快速的进行产品迭代。
-4、【微服务】当然,因为采用的是API模式,所以同样适用于微服务项目,实现高并发的产品需求。
-
-
-
-### 市场前景
-1、前后端分离模式已经是目前的主流开发模式,框架已经是一套可行的方案,开箱即用。
-2、拥有几十篇技术文档和3000人的技术社区,方便快捷的解决问题。
-3、目前已经有超过20多家公司在生产环境中使用,当然实际中更多,具体查看 [点击查看使用的情况](https://github.com/anjoy8/Blog.Core/issues/75)。
-4、同时可以搭配自己的业务,实现微服务的开发,在大数据高并发中,占有更好的优势。
-5、本项目直接作者由微软MVP“老张的哲学”出品,并长久维护,不会断更,有保障。
-
-
-
-## 功能与进度
-
-框架模块:
-- [√] 采用`仓储+服务+接口`的形式封装框架;
-- [√] 异步 async/await 开发;
-- [√] 接入国产数据库ORM组件 —— SqlSugar,封装数据库操作;
-- [√] 支持自由切换多种数据库,MySql/SqlServer/Sqlite/Oracle/Postgresql/达梦/人大金仓;
-- [√] 实现项目启动,自动生成种子数据 ✨;
-- [√] 五种日志记录,审计/异常/请求响应/服务操作/Sql记录等;
-- [√] 支持项目事务处理(若要分布式,用cap即可)✨;
-- [√] 设计4种 AOP 切面编程,功能涵盖:日志、缓存、审计、事务 ✨;
-- [√] 支持 T4 代码模板,自动生成每层代码;
-- [√] 或使用 DbFirst 一键创建自己项目的四层文件(支持多库);
-- [√] 封装`Blog.Core.Webapi.Template`项目模板,一键重建自己的项目 ✨;
-- [√] 搭配多个前端案例供参考和借鉴:Blog.Vue、Blog.Admin、Nuxt.tbug、Blog.Mvp.Blazor ✨;
-- [√] 统一集成 IdentityServer4 认证 ✨;
-
-组件模块:
-- [√] 提供 Redis 做缓存处理;
-- [√] 使用 Swagger 做api文档;
-- [√] 使用 MiniProfiler 做接口性能分析 ✨;
-- [√] 使用 Automapper 处理对象映射;
-- [√] 使用 AutoFac 做依赖注入容器,并提供批量服务注入 ✨;
-- [√] 支持 CORS 跨域;
-- [√] 封装 JWT 自定义策略授权;
-- [√] 使用 Log4Net 日志框架,集成原生 ILogger 接口做日志记录;
-- [√] 使用 SignalR 双工通讯 ✨;
-- [√] 添加 IpRateLimiting 做 API 限流处理;
-- [√] 使用 Quartz.net 做任务调度(目前单机多任务,集群调度暂不支持);
-- [√] 支持 数据库`读写分离`和多库操作 ✨;
-- [√] 新增 Redis 消息队列 ✨;
-- [√] 新增 RabbitMQ 消息队列 ✨;
-- [√] 新增 EventBus 事件总线 ✨;
-- [√] 新增 实现聚合支付;
-- [ ] 计划 - 数据部门权限;
-- [ ] 计划 - ES 搜索;
-
-微服务模块:
-- [√] 可配合 Docker 实现容器化;
-- [√] 可配合 Jenkins 实现CI / CD;
-- [√] 可配合 Consul 实现服务发现;
-- [√] 可配合 Ocelot 实现网关处理;
-- [√] 可配合 Nginx 实现负载均衡;
-- [√] 可配合 Ids4 实现认证中心;
-
-
-
-
-
-
-
diff --git a/.docs/contents/guide/cheat-sheet.md b/.docs/contents/guide/cheat-sheet.md
deleted file mode 100644
index 36f95e36..00000000
--- a/.docs/contents/guide/cheat-sheet.md
+++ /dev/null
@@ -1,580 +0,0 @@
-# Z 主要知识点
-
-
-
-## AOP
-
-本项目多处采用面向切面编程思想——AOP,除了广义上的过滤器和中间件以外,主要通过动态代理的形式来实现AOP编程思想,主要的案例共有四个,分别是:
-1、服务日志AOP;
-2、服务InMemory缓存AOP;
-3、服务Redis缓存AOP;
-4、服务事务AOP;
-
-
-具体的代码可以在 `Blog.Core\Blog.Core\AOP` 文件夹下查看。
-
-与此同时,多个AOP也设置了阀门来控制是否开启,具体的可以查看 `appsettings.json` 中的:
-
-```
- "AppSettings": {
- "RedisCachingAOP": {
- "Enabled": false,
- "ConnectionString": "127.0.0.1:6319"
- },
- "MemoryCachingAOP": {
- "Enabled": true
- },
- "LogAOP": {
- "Enabled": false
- },
- "TranAOP": {
- "Enabled": false
- },
- "SqlAOP": {
- "Enabled": false
- }
- },
-
-```
-
-## Appsettings
-
-整个系统通过一个封装的操作类 `Appsettings.cs` 来控制配置文件 `appsettings.json` 文件,
-操作类地址在:`\Blog.Core.Common\Helper` 文件夹下。
-具体的使用方法是:
-
-```
-Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" })
-
-// 里边的参数,按照 appsettings.json 中设置的层级顺序来写,可以获取到指定的任意内容。
-
-```
-
-
-
-## AspNetCoreRateLimit
-
-系统使用 `AspNetCoreRateLimit` 组件来实现ip限流:
-1、添加 `nuget` 包:
-```
-
-```
-
-2、注入服务 `IpPolicyRateLimitSetup.cs`
-```
-services.AddIpPolicyRateLimitSetup(Configuration);
-```
-
-3、配置中间件
-```
- // Ip限流,尽量放管道外层
- app.UseIpRateLimiting();
-```
-
-4、配置数据
-
-具体的内容,自行百度即可
-```
- "IpRateLimiting": {
- "EnableEndpointRateLimiting": true,
- "StackBlockedRequests": false,
- "RealIpHeader": "X-Real-IP",
- "ClientIdHeader": "X-ClientId",
- "HttpStatusCode": 429,//返回状态码
- "GeneralRules": [//规则,结尾一定要带*
- {
- "Endpoint": "*",
- "Period": "1m",
- "Limit": 120
- },
- {
- "Endpoint": "*:/api/blog*",
- "Period": "1m",
- "Limit": 30
- }
- ]
-
- }
-```
-
-
-
-## Async-Await
-
-整个系统采用 async/await 异步编程,符合主流的开发模式,
-特别是对多线程开发很友好。
-
-
-
-## Authorization-Ids4
-
-本系统 v2.0 版本(目前的系统已经集成 `ids4` 和 `jwt`,并且可以自由切换),已经支持了统一授权认证,和 `blog` 项目、`Admin` 项目、`DDD` 项目等一起,使用一个统一的认证中心。
-
-具体的代码参考:`.\Blog.Core\Extensions` 文件夹下的 `Authorization_Ids4Setup.cs` ,注意需要引用指定的 `nuget` 包,核心代码如下:
-
-```
- //【认证】
- services.AddAuthentication(o =>
- {
- o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
- o.DefaultChallengeScheme = nameof(ApiResponseHandler);
- o.DefaultForbidScheme = nameof(ApiResponseHandler);
- })
- // 2.添加Identityserver4认证
- .AddIdentityServerAuthentication(options =>
- {
- options.Authority = Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" });
- options.RequireHttpsMetadata = false;
- options.ApiName = Appsettings.app(new string[] { "Startup", "IdentityServer4", "ApiName" });
- options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
- options.ApiSecret = "api_secret";
-
- })
-
-
-```
-
-### 如何在Swagger中配置Ids4?
-很简单,直接在 `SwaggerSetup.cs` 中直接接入 `oauth、Implicit` 即可:
-
-```
- //接入identityserver4
- c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
- {
- Type = SecuritySchemeType.OAuth2,
- Flows = new OpenApiOAuthFlows
- {
- Implicit = new OpenApiOAuthFlow
- {
- AuthorizationUrl = new Uri($"{Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"),
- Scopes = new Dictionary {
- {
- "blog.core.api","ApiResource id"
- }
- }
- }
- }
- });
-
-```
-
-然后在 `IdentityServer4` 项目中,做指定的修改,配置 `9291` 的回调地址:
-
-```
- new Client {
- ClientId = "blogadminjs",
- ClientName = "Blog.Admin JavaScript Client",
- AllowedGrantTypes = GrantTypes.Implicit,
- AllowAccessTokensViaBrowser = true,
-
- RedirectUris =
- {
- "http://vueadmin.neters.club/callback",
- // 这里要配置回调地址
- "http://localhost:9291/oauth2-redirect.html"
- },
- PostLogoutRedirectUris = { "http://vueadmin.neters.club" },
- AllowedCorsOrigins = { "http://vueadmin.neters.club" },
-
- AllowedScopes = {
- IdentityServerConstants.StandardScopes.OpenId,
- IdentityServerConstants.StandardScopes.Profile,
- "roles",
- "blog.core.api"
- }
- },
-
-```
-
-然后再 `Swagger` 中,配置登录授权:
-
-
-
-
-## Authorization-JWT
-
-如果你不想使用 `IdentityServer4` 的话,也可以使用 `JWT` 认证,同样是是`Blog.Core\Blog.Core\Extensions` 文件夹下的 `AuthorizationSetup.cs` 中有关认证的部分:
-
-```
- 1.添加JwtBearer认证服务
-.AddJwtBearer(o =>
-{
- 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;
- }
- };
-})
-
-```
-
-
-## AutoMapper
-
-使用 `AutoMapper` 组件来实现 `Dto` 模型的传输转换,具体的用法,可以查看:
-`Blog.Core\Blog.Core\Extensions` 文件夹下的 `AutoMapperSetup.cs` 扩展类,
-通过引用 `AutoMapper` 和 `AutoMapper.Extensions.Microsoft.DependencyInjection` 两个 `nuget` 包,并设置指定的 `profile` 文件,来实现模型转换控制。
-
-```
-// 比如如何定义:
- public class CustomProfile : Profile
- {
- ///
- /// 配置构造函数,用来创建关系映射
- ///
- public CustomProfile()
- {
- CreateMap();
- CreateMap();
- }
- }
-
-
-// 比如如何使用
-models = _mapper.Map(blogArticle);
-
-```
-
-具体的查看项目中代码即可。
-
-
-
-
-## CORS
-
-在线项目使用的是 `nginx` 跨域代理,但是同时也是支持 `CORS` 代理:
-1、注入服务 `services.AddCorsSetup();` 具体代码 `Blog.Core\Blog.Core\Extensions` 文件夹下的 `CorsSetup.cs` 扩展类;
-2、配置中间件 `app.UseCors("LimitRequests");` ,要注意中间件顺序;
-3、配置自己项目的前端端口,通过在 `appsettings.json` 文件中配置自己的前端项目 `ip:端口` ,来实现跨域:
-
-```
- "Startup": {
- "Cors": {
- "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818"
- }
- },
-
-```
-
-
-## DI-AutoFac
-
-项目使用了依赖注入,除了原生的依赖注入以外,更多的使用的是第三方组件 `Autofac` :
-1、引用依赖包:
-```
-
-
-
-```
-主要是第一个 `nuget` 包,下边的是为了实现动态代理 `AOP` 操作;
-
-2、项目之间采用引用解耦的方式,通过反射来注入服务层和仓储层的程序集 `dll` 来实现批量注入,更方便,以后每次新增和修改 `Service` 层和 `Repository` 层,只需要 `F6` 编译一下即可,具体代码查看 `Startup.cs`:
-
-```
-
-
- // 注意在CreateDefaultBuilder中,添加Autofac服务工厂
- public void ConfigureContainer(ContainerBuilder builder)
- {
- var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
- //builder.RegisterType().As();
-
-
- #region 带有接口层的服务注入
-
-
- var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll");
- var repositoryDllFile = Path.Combine(basePath, "Blog.Core.Repository.dll");
-
- if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
- {
- throw new Exception("Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。");
- }
-
-
-
- // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
- var cacheType = new List();
- if (Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool())
- {
- builder.RegisterType();
- cacheType.Add(typeof(BlogRedisCacheAOP));
- }
- if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
- {
- builder.RegisterType();
- cacheType.Add(typeof(BlogCacheAOP));
- }
- if (Appsettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool())
- {
- builder.RegisterType();
- cacheType.Add(typeof(BlogTranAOP));
- }
- if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
- {
- builder.RegisterType();
- cacheType.Add(typeof(BlogLogAOP));
- }
-
- // 获取 Service.dll 程序集服务,并注册
- var assemblysServices = Assembly.LoadFrom(servicesDllFile);
- builder.RegisterAssemblyTypes(assemblysServices)
- .AsImplementedInterfaces()
- .InstancePerDependency()
- .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
- .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。
-
- // 获取 Repository.dll 程序集服务,并注册
- var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
- builder.RegisterAssemblyTypes(assemblysRepository)
- .AsImplementedInterfaces()
- .InstancePerDependency();
-
- #endregion
-
- #region 没有接口层的服务层注入
-
- //因为没有接口层,所以不能实现解耦,只能用 Load 方法。
- //注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法
- //var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services");
- //builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);
-
- #endregion
-
- #region 没有接口的单独类 class 注入
-
- //只能注入该类中的虚方法
- builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
- .EnableClassInterceptors()
- .InterceptedBy(cacheType.ToArray());
-
- #endregion
-
-
- // 这里和注入没关系,只是获取注册列表,请忽略
- tsDIAutofac.AddRange(assemblysServices.GetTypes().ToList());
- tsDIAutofac.AddRange(assemblysRepository.GetTypes().ToList());
- }
-
-```
-
-3、然后 `Program.cs` 中也要加一句话:` .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS `
-
-
-
-## DI-NetCore
-
-除了主要的 `Autofac` 依赖注入以外,也减少的使用了原生的依赖注入方式,很简单,比如这样的:
-```
-
- services.AddSingleton();
- // 注入权限处理器
- services.AddScoped();
- services.AddSingleton(permissionRequirement);
-```
-
-
-## Filter
-
-项目中一共有四个过滤器
-```
-1、GlobalAuthorizeFilter.cs —— 全局授权配置,添加后,就可以不用在每一个控制器上添加 [Authorize] 特性,但是3.1版本好像有些问题,【暂时放弃使用】;
-2、GlobalExceptionFilter.cs —— 全局异常处理,实现 actionContext 级别的异常日志收集;
-3、GlobalRoutePrefixFilter.cs —— 全局路由前缀公约,统计在路由上加上前缀;
-4、UseServiceDIAttribute.cs —— 测试注入,【暂时无用】;
-```
-文件地址在 `.\Blog.Core\Filter` 文件夹下,其中核心的是 `2` 个,重点使用的是 `1` 个 —— 全局异常错误日志 `GlobalExceptionsFilter`:
-通过注册在 `MVC` 服务 `services.AddControllers()` 中,实现全局异常过滤:
-```
- services.AddControllers(o =>
- {
- // 全局异常过滤
- o.Filters.Add(typeof(GlobalExceptionsFilter));
- // 全局路由权限公约
- //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention());
- // 全局路由前缀,统一修改路由
- o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name)));
- })
-```
-
-
-
-## Framework
-
-项目采用 `服务+仓储+接口` 的多层结构,使用依赖注入,并且通过解耦项目,较完整的实现了 `DIP` 原则:
-高层模块不应该依赖于底层模块,二者都应该依赖于抽象。
-抽象不应该依赖于细节,细节应该依赖于抽象。
-
-同时项目也封装了:
-`CodeFirst` 初始化数据库以及数据;
-`DbFirst` 根据数据库(支持多库),生成多层代码,算是简单代码生成器;
-其他功能,[核心功能与进度](http://apk.neters.club/.doc/guide/#%E5%8A%9F%E8%83%BD%E4%B8%8E%E8%BF%9B%E5%BA%A6)
-
-
-
-
-## Log
-
-通过集成 `Log4Net` 组件,完美配合 `NetCore` 官方的 `ILogger` 接口,实现对日志的管控,引用 `nuget` 包 `Microsoft.Extensions.Logging.Log4Net.AspNetCore`:
-Program.cs
-```
- webBuilder
- .UseStartup()
- .ConfigureLogging((hostingContext, builder) =>
- {
- //该方法需要引入Microsoft.Extensions.Logging名称空间
- builder.AddFilter("System", LogLevel.Error); //过滤掉系统默认的一些日志
- builder.AddFilter("Microsoft", LogLevel.Error);//过滤掉系统默认的一些日志
-
- //添加Log4Net
- //var path = Directory.GetCurrentDirectory() + "\\log4net.config";
- //不带参数:表示log4net.config的配置文件就在应用程序根目录下,也可以指定配置文件的路径
- //需要添加nuget包:Microsoft.Extensions.Logging.Log4Net.AspNetCore
- builder.AddLog4Net();
- });
-
-```
-
-然后直接在需要的地方注入使用,比如在控制器中
-` public UserController(ILogger logger)`
-
-然后就可以使用了。
-
-> 注意:日志 其实是分为两部分的:
-> netcore输出(控制台、输出窗口等) 和 `ILogger` 持久化
-> 两者对应配置也不一样,就比如上边的过滤,是针对日志持久化的,如果想要对控制台进行控制,需要配置 `appsettings.json` 中的 `Logging` 节点
-
-
-## MemoryCache
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-
-## Middleware
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## MiniProfiler
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-
-## publish
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-
-
-## Redis
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## Repository
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## SeedData
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## SignalR
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## SqlSugar
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## SqlSugar-Codefirst&DataSeed
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## SqlSugar-SqlAOP
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## Swagger
-
-精力有限,还是更新中...
-如果你愿意帮忙,可以直接在GitHub中,提交pull request,
-我会在后边的贡献者页面里,列出你的名字和项目地址做推广
-## T4
-
-项目集成 `T4` 模板 `.\Blog.Core.FrameWork` 层,目的是可以一键生成项目模板代码。
-1、需要在 `DbHelper.ttinclude` 中配置连接数据库连接字符串;
-2、针对每一层的代码,就去指定的 `.tt` 模板,直接 `CTRL+S` 保存即可;
-
-> 注意,目前的代码是 `SqlServer` 版本的,其他数据库版本的,可以去群文件查看。
-
-
-## Test-xUnit
-
-项目简单使用了单元测试,通过 `xUnit` 组件,具体的可以查看 `Blog.Core.Tests` 层相关代码。
-目前单元测试用例还比较少,大家可以自行添加。
-
-
-## Temple-Nuget
-
-本项目封装了 `Nuget` 自定义模板,你可以根据这个模板,一键创建自己的项目名,具体的操作,可以双击项目根目录下的 `CreateYourProject.bat` ,可以参考 [#如何项目重命名](http://apk.neters.club/.doc/guide/getting-started.html#%E5%A6%82%E4%BD%95%E9%A1%B9%E7%9B%AE%E9%87%8D%E5%91%BD%E5%90%8D)
-
-同时,你也可以再 `Nuget` 管理器中,搜索到:
-
-
-
-
-## UserInfo
-
-
-项目中封装了获取用户信息的代码:
-在 `.\Blog.Core.Common\HttpContextUser` 文件夹下 `AspNetUser.cs` 实现类和 `IUser.cs` 接口。
-
-如果使用,首先需要注册相应的服务,参见:`.\Blog.Core\Extensions` 文件夹下的 `HttpContextSetup.cs`;
-然后,就直接在控制器构造函数中,注入接口 `IUser` 即可;
-
-> `注意`:
-> 1、如果要想获取指定的服务,必须登录,也就是必须要在 `Header` 中传递有效 `Token` ,这是肯定的。
-> 2、如果要获取用户信息,一定要在中间件 `app.UseAuthentication()` 之后(不要问为什么),控制器肯定在它之后,所以能获取到;
-> 3、`【并不是】`一定需要添加 `[Authorize]` 特性,如果你加了这个特性,可以直接获取,但是如果不加,可以从我的 `AspNetUser.cs` 方法中,有一个直接从 `Header` 中解析的方法 `List GetUserInfoFromToken(string ClaimType);`:
-
-```
- public string GetToken()
- {
- return _accessor.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
- }
-
- public List GetUserInfoFromToken(string ClaimType)
- {
-
- var jwtHandler = new JwtSecurityTokenHandler();
- if (!string.IsNullOrEmpty(GetToken()))
- {
- JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(GetToken());
-
- return (from item in jwtToken.Claims
- where item.Type == ClaimType
- select item.Value).ToList();
- }
- else
- {
- return new List() { };
- }
- }
-
-```
diff --git a/.docs/contents/guide/function-sheet.md b/.docs/contents/guide/function-sheet.md
deleted file mode 100644
index d81ad13f..00000000
--- a/.docs/contents/guide/function-sheet.md
+++ /dev/null
@@ -1,471 +0,0 @@
-# H 核心功能一览表
-
-## 一、表结构解析
-
-`Blog.Core` 项目共包含四部分的数据库表结构,分别是:用户角色管理部分、接口菜单权限管理部分、博客文章管理部分、以及其他不重要部分。
-> 注意:目前不提供与维护数据库数据,直接通过 `SeedData` 生成种子数据;
-
-### 1、用户角色管理部分[必须]
-主要是三个表:分别对应用户表(sysUserInfo)、角色表(Role)、用户角色关系表(UserRole)。
-
-
-
-
-
-### 2、接口菜单权限管理部分[必须]
-
-主要是四个表:分别对应接口表(Module)、菜单表(Permission)、接口菜单关系表(ModulePermission)暂时没用到、角色接口菜单关系表(RoleModulePermission)。
-
-
-
-
-
-
-### 3、博客文章管理部分[可选]
-主要是三个表:分别对应博客表(BlogArticle)、Bug专题表(Topic)、Bug内容表(TopicDetail)。
-
-
-
-
-
-
-### 4、其他不重要部分
-
-主要是三个表:分别对应Job调度表(TasksQz)、密码库表(PasswordLib)、操作日志表(OperateLog)、广告表(Advertisement)、公告表(Guestbook)。
-
-
-
-
-
-
-
-
-## 二、日志记录
-
-本框架涵盖了不同领域的日志记录,共五个,分别是:
-
-1、全局异常日志
-
- 开启方式:无需操作。
- 文件路径:web目录下,Log/GlobalExcepLogs_{日期}.log。
- 功能描述:记录项目启动后出现的所有异常日志,不包括中间件中异常。
-
-
-2、IP 请求日志
-
- 开启方式:无需操作。
- 文件路径:web目录下,Log/RequestIpInfoLog.log。
- 功能描述:记录项目启动后客户端请求的ip和接口信息。
- 举例来说:
- {"Ip":"xxx.xx.xx.x","Url":"/api/values","Datetime":"2020-01-06 18:02:19","Date":"2020-01-06","Week":"周一"}
-
-
-3、用户API访问日志
-
- 开启方式:appsettings.json -> Middlewar -> RecordAccessLogs 节点为true。
- 文件路径:web目录下,Log/RecordAccessLogs_{日期}.log。
- 功能描述:记录项目启动后客户端所有的API访问日志,包括参数、body以及用户信息。
-
-
-4、服务层请求响应AOP日志
-
- 开启方式:appsettings.json -> AppSettings -> LogAOP 节点为true。
- 文件路径:web目录下,Log/AOPLog.log。
- 功能描述:记录项目启动请求api后,所有的service层日志,包括方法名、参数、响应结果或用户(非必须)。
-
-
-5、数据库操作日志
-
- 开启方式:appsettings.json -> AppSettings -> SqlAOP 节点为true。
- 文件路径:web目录下,Log/SqlLog.log。
- 功能描述:记录项目启动请求api并访问service后,所有的db操作日志,包括Sql参数与Sql语句。
- 举例来说:
- --------------------------------
- 1/6/2020 6:13:04 PM|
- 【SQL参数】:@bID0:1
- 【SQL语句】:SELECT `bID`,`bsubmitter`,`btitle`,`bcategory`,`bcontent`,`btraffic`,`bcommentNum`,`bUpdateTime`,`bCreateTime`,`bRemark`,`IsDeleted` FROM `BlogArticle` WHERE ( `bID` = @bID0 )
-
-
- ## 三、控制台信息展示
-
-
-
-
-
- ## 四、Nginx一览表
-
-
-
-```
-#user nobody;
-worker_processes 1;
-
-#error_log logs/error.log;
-#error_log logs/error.log notice;
-#error_log logs/error.log info;
-
-#pid logs/nginx.pid;
-events {
- worker_connections 1024;
-}
-
-http {
- include mime.types;
- default_type application/octet-stream;
- server_names_hash_bucket_size 64;
-
- #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- # '$status $body_bytes_sent "$http_referer" '
- # '"$http_user_agent" "$http_x_forwarded_for"';
-
- #access_log logs/access.log main;
- sendfile on;
- #tcp_nopush on;
-
- #keepalive_timeout 0;
- keepalive_timeout 600;
- proxy_read_timeout 600;
- proxy_send_timeout 600;
-
- proxy_buffer_size 128k;
- proxy_buffers 32 32k;
- proxy_busy_buffers_size 128k;
-
- #gzip on;
-
- ######################################################################
- server {
- listen 80;
- server_name www.neters.club;
-
- #charset koi8-r;
-
- #access_log logs/host.access.log main;
- location / {
- root C:\code\Code\Neters\home;
- index index.html index.htm;
- }
- }
-
- server {
- listen 80;
- server_name neters.club;
-
- #charset koi8-r;
-
- #access_log logs/host.access.log main;
- location / {
- root C:\code\Code\Neters\home;
-
- index index.html index.htm;
- }
- }
-
- server {
- listen 80;
- server_name ids.neters.club;
- rewrite ^(.*)$ https://$host$1 permanent;#把http的域名请求转成https,第二种写法在此节的末端
-
- #charset koi8-r;
-
- #access_log logs/host.access.log main;
- location / {
- #proxy_pass http://localhost:5004;
- root html;
- index index.html index.htm;
- }
- }
-
- server {
- listen 443 ssl;
- server_name ids.neters.club; #网站域名,和80端口保持一致
- ssl on;
- ssl_certificate 1_ids.neters.club_bundle.crt; #证书公钥
- ssl_certificate_key 2_ids.neters.club.key; #证书私钥
- ssl_session_cache shared:SSL:1m;
- ssl_session_timeout 5m;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_ciphers ECDH:AESGCM:HIGH:!RC4:!DH:!MD5:!3DES:!aNULL:!eNULL;
- ssl_prefer_server_ciphers on;
-
- error_page 497 https://$host$uri?$args;
-
- location / {
- proxy_pass http://localhost:5004;
- proxy_redirect off;
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-
- proxy_set_header Cookie $http_cookie;
- #proxy_cookie_path
- chunked_transfer_encoding off;
- }
- }
-
- server {
- listen 80;
- server_name apk.neters.club;
-
- #charset koi8-r;
-
- #access_log logs/host.access.log main;
- location / {
- root html;
- proxy_pass http://localhost:9291;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection keep-alive;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_cache_bypass $http_upgrade;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-
- index index.html index.htm;
- }
-
- location /.doc/ {
- proxy_pass http://docs.neters.club/;
- }
- }
-
- server {
- listen 80;
- server_name docs.neters.club;
-
- location / {
- root C:\code\Code\Blog.Core\.docs\contents\.vuepress\dist;
- index index.html index.htm;
- }
- }
-
- server {
- listen 80;
- server_name vueadmin.neters.club;
-
- location / {
- try_files $uri $uri/ /index.html;
- root C:\code\Code\Blog.Admin\distis;
- #proxy_pass http://localhost:2364;
- index index.html index.htm;
- }
-
- location /api/ {
- rewrite ^.+apb/?(.*)$ /$1 break;
- include uwsgi_params;
- proxy_pass http://localhost:9291;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- #proxy_set_header Connection "upgrade";
- #proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
-
- location /api2/ {
- rewrite ^.+apb/?(.*)$ /$1 break;
- include uwsgi_params;
- proxy_pass http://localhost:9291;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
-
- location /images/ {
- include uwsgi_params;
- proxy_pass http://localhost:9291;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- #proxy_set_header Connection "upgrade";
- #proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
- location /.doc/ {
- proxy_pass http://docsadmin.neters.club/;
- }
-
- error_page 404 /404.html;
-
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
-
- server {
- listen 80;
- server_name docsadmin.neters.club;
-
- location / {
- root C:\code\Code\Blog.Admin\.doc\contents\.vuepress\dist;
- index index.html index.htm;
- }
- }
-
-
- server {
- listen 80;
- server_name ddd.neters.club;
- location / {
- proxy_pass http://localhost:4773;
- index index.php index.html index.htm;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection keep-alive;
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- }
- }
-
-
- server {
- listen 80;
- server_name ask.neters.club;
-
- #charset koi8-r;
-
- #access_log logs/host.access.log main;
- location / {
- root html;
- proxy_pass http://localhost:5020;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- #proxy_set_header Connection "upgrade";
- #proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- index index.html index.htm;
- }
- }
-
-
- server {
- listen 80;
- server_name vueblog.neters.club;
-
- location / {
- try_files $uri $uri/ /index.html;
- root C:\code\Code\Blog.Vue\dist;
- index index.html index.htm;
- }
-
-
- location /api {
- rewrite ^.+apb/?(.*)$ /$1 break;
- include uwsgi_params;
- proxy_pass http://localhost:9291;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
-
-
- location /images {
- include uwsgi_params;
- proxy_pass http://localhost:9291;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
-
- error_page 404 /404.html;
-
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
-
- upstream nodenuxt {
- server 127.0.0.1:3089; # nuxt 项目监听PC端端口
- keepalive 64;
- }
- server {
- listen 80;
- server_name tibug.neters.club;
-
- location / {
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header Host $host;
- proxy_set_header X-Nginx-Proxy true;
- proxy_cache_bypass $http_upgrade;
- proxy_pass http://nodenuxt;
- }
-
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
-
- server {
- listen 80;
- server_name jwt.neters.club;
-
- location / {
- root C:\code\Code\jwttoken;
- index index.html index.htm;
- }
-
- error_page 404 /404.html;
-
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
-}
-
-```
-> 这里说明下,我的 `Nginx` 文件中,`Ids4` 项目强制使用 `Https` ,采用的是直接跳转,这也是一个办法,当然还有第二种办法(感谢 `tibos`):
-```
-server {
- listen 80;
- server_name admin.wmowm.com;
- location / {
- proxy_pass http://localhost:9002;
- index index.php index.html index.htm;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection keep-alive;
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- }
-}
-
-server {
- listen 443 ssl;#监听443端口(https默认端口)
- server_name admin.wmowm.com; #填写绑定证书的域名
- ssl_certificate /etc/nginx/conf.d/key/admin.wm.crt;#填写你的证书所在的位置
- ssl_certificate_key /etc/nginx/conf.d/key/admin.wm.key;#填写你的key所在的位置
- ssl_session_timeout 5m;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置
- ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置
- ssl_prefer_server_ciphers on;
- location / {
- proxy_pass http://localhost:9002;
- index index.php index.html index.htm;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection keep-alive;
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
-}
-```
\ No newline at end of file
diff --git a/.docs/contents/guide/getting-started.md b/.docs/contents/guide/getting-started.md
deleted file mode 100644
index 9c4f6b0d..00000000
--- a/.docs/contents/guide/getting-started.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# K 快速上手
-注意
-
-请确保你的 `Visual Studio 2019` 版本 >= `16.8.2`。
-并安装 `.NET 5.0 SDK`
-
-
-## 下载
-Github(国际) 下载 [https://github.com/anjoy8/Blog.Core](https://github.com/anjoy8/Blog.Core)
-
-Gitee(国内) 下载 [https://gitee.com/laozhangIsPhi/Blog.Core](https://gitee.com/laozhangIsPhi/Blog.Core)
-
-
-## 编译与运行
-1、拿到项目后,双击 `Blog.Core.sln` 解决方案;
-2、首先 `F6` 编译,看是否有错误;
-3、然后 `F5` 运行,调起 `9291` 端口,浏览器查看效果;
-4、因为系统默认的是 `sqlite` 数据库,如果你想换其他数据库,请看下边;
-5、注意:本系统是直接自动生成数据库和数据的,不用手动创建数据库;
-
-
-
-
-## CodeFirst 与 DbFirst
-1、项目同时支持两个常见开发模式:`CodeFirst` 和 `DbFirst`;
-2、首先 如果你是第一次下载我的项目,肯定是想要浏览效果和直接使用对应的权限相关的内容,这个时候肯定需要用到数据库表结构,那就肯定需要 `CodeFirst` ,只需要在`appsettings.json` 里配置好数据库连接字符串(下文会说到如何配置),就能正确运行;
-3、浏览器查看效果,或者配合 `Admin` 项目查看效果后,如果感觉项目可行,并打算在此基础上二次开发,那肯定会在你刚刚创建的数据库种去创建新的表结构,这个时候就需要使用 `DbFirst` 模式,来生成四层项目问题:Model+Service+Repository等;
-4、你可以使用T4模板,但是我更建议使用 `/api/DbFirst/GetFrameFiles` 接口来生成,不仅支持多种类型的数据库,还支持同时多库模式的输出;
-5、如果你不想用我的表结构和实体类,在项目启动的时候,把配置文件的 `SeedDBEnabled`节点设置成False即可,然后配置对应的你自己的数据库连接字符串,比如是商城的,然后使用 `/api/DbFirst/GetFrameFiles` 接口来生成你的数据库四层类文件;
-
-
-
-## 如何配置数据库连接字符串
-
-1、打开 `Blog.Core` 项目下的 `appsettings.json` 文件;
-2、修改 `DBS` 字节内容,配置对应的连接字符串,注意`DBType`对应不同的数据库类型;
-3、把你想要运行的数据库 `Enabled` 为 `true` 即可,其他都要设置 `false`;
-4、然后 `MainDB` 设置为下边你使用的指定 `ConnId`:
-
-```
- "MainDB": "WMBLOG_MSSQL", //当前项目的主库,所对应的连接字符串的Enabled必须为true
- "MutiDBEnabled": false, //是否开启多库
- "DBS": [
- {
- "ConnId": "WMBLOG_SQLITE",
- "DBType": 2,// sqlite数据库
- "Enabled": true,// 设置为true,启用1
- "Connection": "WMBlog.db" //只写数据库名就行
- },
- {
- "ConnId": "WMBLOG_MSSQL",
- "DBType": 1,// sqlserver数据库
- "Enabled": true,// 设置为true,启用2
- "Connection": "Server=.;Database=WMBlogDB;User ID=sa;Password=123;",
- "ProviderName": "System.Data.SqlClient"
- },
- {
- "ConnId": "WMBLOG_MYSQL",
- "DBType": 0,// mysql
- "Enabled": false,// false 不启用
- "Connection": "Server=localhost; Port=3306;Stmt=; Database=wmblogdb; Uid=root; Pwd=456;"
- },
- {
- "ConnId": "WMBLOG_ORACLE",
- "DBType": 3,// Oracle
- "Enabled": false,// 不启用
- "Connection": "Provider=OraOLEDB.Oracle; Data Source=WMBlogDB; User Id=sss; Password=789;"
- }
- ],
-```
-
-
-5、如果你想多库操作,需要配置
-```
- a:MainDB 设置为主库的 ConnId;
- b:MutiDBEnabled设置为true,
- c:把下边想要连接的多个连接字符串都设置为true
-```
-
-## 如何配置项目端口号
-1、在 `Blog.Core` 层下的 `program.cs` 文件中,将 `9291`端口,修改为自己想要的端口号;
-2、或者在 `launchSettings.json` 中设置(`注意,如果仅仅修改这里,publish后,端口访问无效`);
-
-## 如何项目重命名
-1、双击项目根目录下的 `CreateYourProject.bat` 批处理文件;
-2、根据提示,`在Dos窗口内`输入自己想要的项目名称即可;
-3、在根目录会有一个 `.1YourProject` 文件夹,里边即你的项目;
-
-
-## 新增实体模块后如何迁移到数据库
-1、在 `Blog.Core.Model` 项目目录下的 `Seed` 文件夹下,找到 `DBSeed` 类;
-2、根据提示,找到生成table的地方 `myContext.CreateTableByEntity`;
-3、添加进去你新增的实体类,当然也可以用下边的单独写法;
-4、编译项目,没错后,运行,则数据库更新完毕;
-
-
-## 新增实体,如何进行增删改查CURD操作
-1、随便找一个含有业务逻辑的 `controller` 参考一下即可;
-2、主要 `api` 是通过 `Service` 服务层提供业务逻辑;
-3、然后服务层通过 `Repository` 仓储层封装持久化操作;
-4、每一个表基本上对应一个仓储类,基本的操作都封装到了 `BaseRepository.cs` 基类仓储中;
-5、添加完业务逻辑,记得要 `F6` 重新编译一下,因为项目间引用解耦了;
-6、项目已经自动注入了,直接在控制器使用对应的服务层接口就行: `IxxxxService` ;
-
-
-## 新增数据库表,如何反向生成四层文件
-1、可以通过 `T4` 模板来生成,在 `Blog.Core.FrameWork` 层,使用方法: [9757999.html](https://www.cnblogs.com/laozhang-is-phi/p/9757999.html#autoid-4-3-0) ;
-> 注意:这种方案,目前默认的只能是 `SqlServer` ,其他类型的数据库,可以看上边文章中的代码,或者群文件里对应的代码。
-
-> 1、修改`DbHelper.ttinclude`文件中的连接字符串,注意是`SqlServer`的: public static readonly string ConnectionString;
-> 2、然后去各个层模板文件,点击`Ctrl+S`;
-> 3、就会在对应的层内,看到新文件,比如:Blog.Core.Model/Model_NEW
-
-
-
-2、也可以通过 `Sqlsugar` 所带的方法来实现 `DbFirst`,具体查看 `Controller` 层下的 `DbFirstController.cs`;
-
-3、总体操作过程,可以参考我的视频:[av77612407](https://www.bilibili.com/video/av77612407?p=2) ;
-
-
-## 发布与部署
-1、双击项目根目录下的 `Blog.Core.Publish.bat`批处理文件;
-2、执行完成后,根目录会有一个`.PublishFiles` 文件夹,就是发布后的项目;
-
-
-## 如何更新项目模板
-1、着急的话自己打包,不着急就提 `issue`,等我更新;
-2、我的开源项目中,有个模板项目 `BlogCoreTempl` [地址](https://github.com/anjoy8/BlogCoreTempl),下载下来;
-3、下载最新的 `Blog.Core` 源代码;
-4、将源代码拷贝到模板项目的 `content` 文件夹下;
-5、双击 `Package.bat` 文件,就生成了最新的模板了;
-
diff --git a/.docs/package.json b/.docs/package.json
deleted file mode 100644
index 3f0483bf..00000000
--- a/.docs/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "BCVP",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "keywords": [],
- "author": "",
- "license": "ISC"
-}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000..3df0ef21
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,70 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ master ]
+ schedule:
+ - cron: '32 13 * * 2'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'csharp', 'javascript' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index 80289a98..f11fee1f 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -12,6 +12,12 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 5.0.100
+ dotnet-version: 8.0.x
- name: Build with dotnet
run: dotnet build --configuration Release
+ - name: Build image
+ run: docker build -f "Dockerfile" --force-rm -t laozhangisphi/apkimg .
+ - name: Log into registry
+ run: echo "${{ secrets.ACCESS_TOKEN }}" | docker login -u laozhangisphi --password-stdin
+ - name: Push image
+ run: docker push laozhangisphi/apkimg
diff --git a/.gitignore b/.gitignore
index 9a7ee7a9..99804f89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,9 @@ bld/
# Visual Studio 2017 auto generated files
Generated\ Files/
+# Visual Studio Code
+.vscode
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
@@ -351,3 +354,9 @@ Blog.Core/WMBlog.db
.docs/contents/.vuepress/dist/*
Blog.Core/Blog.Core*.xml
Blog.Core.Api/WMBlog.db
+Blog.Core.Api/wwwroot/ui/
+Blog.Core.Api/Logs
+*.db
+/Blog.Core.Api/WMBlog.db-journal
+.docs/.vuepress/dist/
+Blog.Core.Api/wwwroot/Temp/Sessions
diff --git a/Blog.Core.Api/.config/dotnet-tools.json b/Blog.Core.Api/.config/dotnet-tools.json
new file mode 100644
index 00000000..98091c92
--- /dev/null
+++ b/Blog.Core.Api/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-ef": {
+ "version": "6.0.8",
+ "commands": [
+ "dotnet-ef"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj
index da5c4e05..8bc327ab 100644
--- a/Blog.Core.Api/Blog.Core.Api.csproj
+++ b/Blog.Core.Api/Blog.Core.Api.csproj
@@ -1,14 +1,12 @@
-
Exe
-
- net5.0
-
- OutOfProcess
+ enable
+
Linux
true
+ default
@@ -26,25 +24,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
@@ -57,7 +81,7 @@
-
+
@@ -71,6 +95,9 @@
Always
+
+ PreserveNewest
+
@@ -82,10 +109,14 @@
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml
index 575f738b..00b4c5be 100644
--- a/Blog.Core.Api/Blog.Core.Model.xml
+++ b/Blog.Core.Api/Blog.Core.Model.xml
@@ -4,6 +4,36 @@
Blog.Core.Model
+
+
+ 无任何权限
+
+
+
+
+ 自定义权限
+
+
+
+
+ 本部门
+
+
+
+
+ 本部门及以下
+
+
+
+
+ 仅自己
+
+
+
+
+ 所有
+
+
以下model 来自ids4项目,多库模式,为了调取ids4数据
@@ -165,6 +195,26 @@
返回数据集合
+
+
+ 用户访问趋势日志
+
+
+
+
+ 用户
+
+
+
+
+ 次数
+
+
+
+
+ 更新时间
+
+
广告图片
@@ -251,6 +301,146 @@
逻辑删除
+
+
+ 评论
+
+
+
+
+ 博客文章 评论
+
+
+
+
+ 部门表
+
+
+
+
+ Desc:部门关系编码
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:部门名称
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:负责人
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:排序
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:部门状态(0正常 1停用)
+ Default:0
+ Nullable:True
+
+
+
+
+ Desc:删除标志(0代表存在 2代表删除)
+ Default:0
+ Nullable:True
+
+
+
+
+ Desc:创建者
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:创建时间
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:更新者
+ Default:
+ Nullable:True
+
+
+
+
+ Desc:更新时间
+ Default:
+ Nullable:True
+
+
+
+
+ 用户团队表
+
+
+
+
+ ID
+
+
+
+
+ HttpContext.TraceIdentifier 事件链路ID(获取或设置一个唯一标识符,用于在跟踪日志中表示此请求。)
+
+
+
+
+ 时间
+
+
+
+
+ 线程
+
+
+
+
+ 等级
+
+
+
+
+ 记录器
+
+
+
+
+ 日志类型
+
+
+
+
+ 数据类型
+
+
+
+
+ 错误信息
+
+
+
+
+ 异常
+
+
博客ID
@@ -286,46 +476,6 @@
-
-
- 菜单与按钮关系表
-
-
-
-
- 获取或设置是否禁用,逻辑上的删除,非物理删除
-
-
-
-
- 创建ID
-
-
-
-
- 创建者
-
-
-
-
- 创建时间
-
-
-
-
- 修改ID
-
-
-
-
- 修改者
-
-
-
-
- 修改时间
-
-
接口API地址信息表
@@ -526,6 +676,11 @@
菜单图标
+
+
+ 菜单图标新
+
+
菜单描述
@@ -596,6 +751,17 @@
排序
+
+
+ 自定义权限的部门ids
+
+
+
+
+ 权限范围
+ -1 无任何权限;1 自定义权限;2 本部门;3 本部门及以下;4 仅自己;9 全部;
+
+
是否激活
@@ -671,1321 +837,2570 @@
修改时间
-
+
- 用户信息表
+ 状态
+ 中立字段,某些表可使用某些表不使用
-
+
- 登录账号
+ 中立字段,某些表可使用某些表不使用
+ 逻辑上的删除,非物理删除
+ 例如:单据删除并非直接删除
-
+
- 登录密码
+ 中立字段
+ 是否内置数据
-
+
- 真实姓名
+ 创建ID
-
+
- 状态
+ 创建者
-
+
- 备注
+ 创建时间
-
+
- 创建时间
+ 修改ID
-
+
- 更新时间
+ 更新者
-
-
- 最后登录时间
-
-
-
-
- 错误次数
-
+
+
+ 修改日期
+
-
+
- 登录账号
+ 数据版本
-
+
- 任务计划表
+ 软删除 过滤器
-
+
- 任务名称
+ 系统租户表
+ 根据TenantType 分为两种方案:
+ 1.按租户字段区分
+ 2.按租户分库
+
+
+
+ 注意:
+ 使用租户Id方案,无需配置分库的连接
-
+
- 任务分组
+ 名称
-
+
- 任务运行时间表达式
+ 租户类型
-
+
- 任务所在DLL对应的程序集名称
+ 数据库/租户标识 不可重复
+ 使用Id方案,可无需配置
-
+
- 任务所在类
+ 主机
+ 使用Id方案,可无需配置
-
+
- 任务描述
+ 数据库类型
+ 使用Id方案,可无需配置
-
+
- 执行次数
+ 数据库连接
+ 使用Id方案,可无需配置
-
+
- 开始时间
+ 状态
-
+
- 结束时间
+ 备注
-
+
- 触发器类型(0、simple 1、cron)
+ 用户信息表
-
+
- 执行间隔时间, 秒为单位
+ 登录账号
-
+
- 循环执行次数
+ 登录密码
-
+
- 是否启动
+ 真实姓名
-
+
- 执行传参
+ 状态
-
+
- 创建时间
+ 部门
-
+
- 任务内存中的状态
+ 备注
-
+
- Tibug 类别
+ 创建时间
-
+
- Tibug 博文
+ 更新时间
-
+
- 用户跟角色关联表
+ 关键业务修改时间
-
+
- 获取或设置是否禁用,逻辑上的删除,非物理删除
+ 最后异常时间
-
+
+
+ 错误次数
+
+
+
- 创建ID
+ 登录账号
-
+
- 创建者
+ 租户Id
-
+
- 创建时间
+ 任务日志表
-
+
- 修改ID
+ 任务ID
-
+
- 修改者
+ 任务耗时
-
+
- 修改时间
+ 执行结果(0-失败 1-成功)
-
+
- ID
+ 运行时间
-
+
- 菜单与按钮关系表
- 父类
+ 结束时间
-
+
- 菜单ID
+ 执行参数
-
+
- 按钮ID
+ 异常信息
-
+
- 接口API地址信息表
- 父类
+ 异常堆栈
-
+
- 父ID
+ 创建ID
-
+
- 路由菜单表
+ 创建者
-
+
- 上一级菜单(0表示上一级无菜单)
+ 创建时间
-
+
- 接口api
+ 修改ID
-
+
- 按钮跟权限关联表
- 父类
+ 修改者
-
+
- 角色ID
+ 修改时间
-
+
- 菜单ID
+ 任务名称
-
+
- api ID
+ 任务分组
-
+
- ID
- 泛型主键Tkey
+ 任务计划表
-
+
- 用户信息表
+ 任务名称
-
+
- uID
- 泛型主键Tkey
+ 任务分组
-
+
- Tibug 博文
+ 任务运行时间表达式
-
+
- 用户跟角色关联表
- 父类
+ 任务所在DLL对应的程序集名称
-
+
- 用户ID
+ 任务所在类
-
+
- 角色ID
+ 任务描述
-
+
- 通用分页信息类
+ 执行次数
-
+
- 当前页标
+ 开始时间
-
+
- 总页数
+ 结束时间
-
+
- 数据总数
+ 触发器类型(0、simple 1、cron)
-
+
- 每页大小
+ 执行间隔时间, 秒为单位
-
+
- 返回数据
+ 循环执行次数
-
+
- 无权限
+ 已循环次数
-
+
- 找不到指定资源
+ 是否启动
-
+
- 找不到指定资源
+ 执行传参
-
+
- 异步添加种子数据
+ 创建时间
-
-
-
-
+
- 生成Controller层
+ 任务内存中的状态
- sqlsugar实例
- 数据库链接ID
- 数据库表名数组,默认空,生成所有表
-
-
-
+
- 生成Model层
+ 业务数据
+ 多租户 (Id 隔离)
- sqlsugar实例
- 数据库链接ID
- 数据库表名数组,默认空,生成所有表
-
-
-
+
- 生成IRepository层
+ 无需手动赋值
- sqlsugar实例
- 数据库链接ID
-
- 数据库表名数组,默认空,生成所有表
-
-
+
- 生成 IService 层
+ 名称
- sqlsugar实例
- 数据库链接ID
-
- 数据库表名数组,默认空,生成所有表
-
-
+
- 生成 Repository 层
+ 金额
- sqlsugar实例
- 数据库链接ID
-
- 数据库表名数组,默认空,生成所有表
-
-
+
- 生成 Service 层
+ 多租户-多表方案 业务表 子表
- sqlsugar实例
- 数据库链接ID
-
- 数据库表名数组,默认空,生成所有表
-
-
+
- 功能描述:根据数据库表生产Controller层
- 作 者:Blog.Core
+ 多租户-多表方案 业务表
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
- 是否序列化
-
+
- 功能描述:根据数据库表生产Model层
- 作 者:Blog.Core
+ 名称
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
- 是否序列化
-
+
- 功能描述:根据数据库表生产IRepository层
- 作 者:Blog.Core
+ 金额
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
-
+
- 功能描述:根据数据库表生产IServices层
- 作 者:Blog.Core
+ 多租户-多库方案 业务表
+ 公共库无需标记[MultiTenant]特性
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
-
+
- 功能描述:根据数据库表生产 Repository 层
- 作 者:Blog.Core
+ 名称
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
-
+
- 功能描述:根据数据库表生产 Services 层
- 作 者:Blog.Core
+ 金额
-
- 数据库链接ID
- 实体类存放路径
- 命名空间
- 生产指定的表
- 实现接口
-
-
+
- 根据模板内容批量生成文件
+ Tibug 类别
- 类文件字符串list
- 生成路径
- 文件名格式模板
-
+
- 连接字符串
- Blog.Core
+ Tibug 博文
-
+
- 连接字符串
- Blog.Core
+ 用户跟角色关联表
-
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
- 数据库类型
- Blog.Core
+ 创建ID
-
+
- 数据连接对象
- Blog.Core
+ 创建者
-
+
- 功能描述:构造函数
- 作 者:Blog.Core
+ 创建时间
-
+
- 功能描述:获取数据库处理对象
- 作 者:Blog.Core
+ 修改ID
- 返回值
-
+
- 功能描述:获取数据库处理对象
- 作 者:Blog.Core
+ 修改者
- db
- 返回值
-
+
- 功能描述:根据实体类生成数据库表
- 作 者:Blog.Core
+ 修改时间
- 是否备份表
- 指定的实体
-
+
- 功能描述:根据实体类生成数据库表
- 作 者:Blog.Core
+
- 是否备份表
- 指定的实体
-
+
- 功能描述:设置初始化参数
- 作 者:Blog.Core
-
- 连接字符串
- 数据库类型
+ 公司ID
+
-
+
- 功能描述:创建一个链接配置
- 作 者:Blog.Core
+ 公司名称
- 是否自动关闭连接
- 是否夸类事务
- ConnectionConfig
-
+
- 功能描述:获取一个自定义的DB
- 作 者:Blog.Core
+ 公司IP
- config
- 返回值
-
+
- 功能描述:获取一个自定义的数据库处理对象
- 作 者:Blog.Core
+ 公司备注
- sugarClient
- 返回值
-
+
- 功能描述:获取一个自定义的数据库处理对象
- 作 者:Blog.Core
+ api地址
- config
- 返回值
-
+
- 表格数据,支持分页
+ 是否激活
-
+
- 返回编码
+ 创建者id
-
+
- 返回信息
+ 创建人
-
+
- 记录总数
+ 创建时间
-
+
- 返回数据集
+ 修改者id
-
+
- 广告类
+ 修改人
-
+
- 分类ID
+ 修改时间
-
+
- 创建时间
+
-
+
- 广告图片
+ 微信公众号唯一标识
-
+
- 广告标题
+ 微信公众号名称
-
+
- 广告链接
+ 微信账号
-
+
- 备注
+ 微信名称
-
+
- 博客信息展示类
+ 应用ID
-
+
-
+ 应用秘钥
-
- 创建人
-
+
+
+ 公众号推送token
-
- 博客标题
-
+
+
+ 验证秘钥(验证消息是否真实)
-
- 摘要
-
+
+
+ 微信公众号token过期时间
-
+
- 上一篇
+ 备注
-
+
- 上一篇id
+ 是否激活
-
+
- 下一篇
+ 创建者id
-
+
- 下一篇id
+ 创建人
-
- 类别
-
+
+
+ 创建时间
-
- 内容
-
+
+
+ 修改者id
-
+
- 访问量
+ 修改人
-
+
- 评论数量
+ 修改时间
-
- 修改时间
+
+
-
+
- 创建时间
+ 推送ID
-
- 备注
-
+
+
+ 来自谁
-
+
- 留言信息展示类
+ 推送IP
-
- 留言表
-
+
+
+ 推送客户
-
- 博客ID
-
+
+
+ 推送用户
-
- 创建时间
-
+
+
+ 推送模板ID
-
- 手机
-
+
+
+ 推送内容
-
- qq
-
+
+
+ 推送时间
-
- 留言内容
-
+
+
+ 推送状态(Y/N)
-
- ip地址
-
+
+
+ 备注
-
- 是否显示在前台,0否1是
-
+
+
+ 推送OpenID
-
+
- 商户号
-
+ 推送微信公众号
+
-
+
- 柜台号
-
+ 创建者id
+
-
+
- 分行号
-
+ 创建人
+
-
+
- 集团商户信息
-
+ 创建时间
+
-
+
- 交易码
-
+ 修改者id
+
-
+
- 商户类型
-
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 主键id,ticket
+
+
+
+
+ 需要绑定的公司
+
+
+
+
+ 需要绑定的员工id
+
+
+
+
+ 需要绑定的员工昵称
+
+
+
+
+ 创建时间
+
+
+
+
+ 关联的公众号
+
+
+
+
+ 是否已使用
+
+
+
+
+ 使用时间
+
+
+
+
+ 关联的微信用户id
+
+
+
+
+ 创建者id
+
+
+
+
+ 创建人
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改者id
+
+
+
+
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 来自哪个公众号
+
+
+
+
+ 绑定公司id
+
+
+
+
+ 绑定员工id
+
+
+
+
+ 绑定微信id
+
+
+
+
+ 绑定微信联合id
+
+
+
+
+ 绑定时间
+
+
+
+
+ 更新时间
+
+
+
+
+ 备注
+
+
+
+
+ 是否已解绑
+
+
+
+
+ 上次绑定微信id
+
+
+
+
+ 创建者id
+
+
+
+
+ 创建人
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改者id
+
+
+
+
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 文件ID
+
+
+
+
+ 文件名称
+
+
+
+
+ 文件大小
+
+
+
+
+ 文件类型
+
+
+
+
+ 文件拓展名
+
+
+
+
+ 文件位置
+
+
+
+
+ 文件上传时间
+
+
+
+
+ 文件备注
+
+
+
+
+ 创建者id
+
+
+
+
+ 创建人
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改者id
+
+
+
+
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+ 部门表
+
+
+
+
+ 上一级(0表示无上一级)
+
+
+
+
+ 接口API地址信息表
+ 父类
+
+
+
+
+ 父ID
+
+
+
+
+ 路由菜单表
+
+
+
+
+ 上一级菜单(0表示上一级无菜单)
+
+
+
+
+ 接口api
+
+
+
+
+ 按钮跟权限关联表
+ 父类
+
+
+
+
+ 角色ID
+
+
+
+
+ 菜单ID
+
+
+
+
+ api ID
+
+
+
+
+ ID
+ 泛型主键Tkey
+
+
+
+
+ 用户信息表
+
+
+
+
+ Id
+ 泛型主键Tkey
+
+
+
+
+ Tibug 博文
+
+
+
+
+ 用户跟角色关联表
+ 父类
+
+
+
+
+ 用户ID
+
+
+
+
+ 角色ID
+
+
+
+
+ 通用分页信息类
+
+
+
+
+ 当前页标
+
+
+
+
+ 总页数
+
+
+
+
+ 数据总数
+
+
+
+
+ 每页大小
+
+
+
+
+ 返回数据
+
+
+
+
+ 所需分页参数
+ 作者:胡丁文
+ 时间:2020-4-3 20:31:26
+
+
+
+
+ 当前页
+
+
+
+
+ 每页大小
+
+
+
+
+ 排序字段(例如:id desc,time asc)
+
+
+
+
+ 查询条件( 例如:id = 1 and name = 小明)
+
+
+
+
+ 无权限
+
+
+
+
+ 找不到指定资源
+
+
+
+
+ 找不到指定资源
+
+
+
+
+ 数据库读取类型
+
+
+
+
+ 表格数据,支持分页
+
+
+
+
+ 返回编码
+
+
+
+
+ 返回信息
+
+
+
+
+ 记录总数
+
+
+
+
+ 返回数据集
+
+
+
+
+ 租户模型接口
+
+
+
+
+ 租户Id
+
+
+
+
+ 标识 多租户 的业务表
+ 默认设置是多库
+ 公共表无需区分 直接使用主库 各自业务在各自库中
+
+
+
+
+ 租户隔离方案
+
+
+
+
+ Id隔离
+
+
+
+
+ 库隔离
+
+
+
+
+ 表隔离
+
+
+
+
+ 广告类
+
+
+
+
+ 分类ID
+
+
+
+
+ 创建时间
+
+
+
+
+ 广告图片
+
+
+
+
+ 广告标题
+
+
+
+
+ 广告链接
+
+
+
+
+ 备注
+
+
+
+
+ 博客信息展示类
+
+
+
+
+
+
+
+
+ 创建人
+
+
+
+
+ 博客标题
+
+
+
+
+ 摘要
+
+
+
+
+
+ 上一篇
+
+
+
+
+ 上一篇id
+
+
+
+
+ 下一篇
+
+
+
+
+ 下一篇id
+
+
+
+ 类别
+
+
+
+
+ 内容
+
+
+
+
+
+ 访问量
+
+
+
+
+ 评论数量
+
+
+
+ 修改时间
+
+
+
+
+
+ 创建时间
+
+
+
+ 备注
+
+
+
+
+
+ Type Description balabala
+
+
+
+
+ 留言信息展示类
+
+
+
+ 留言表
+
+
+
+
+ 博客ID
+
+
+
+
+ 创建时间
+
+
+
+
+ 手机
+
+
+
+
+ qq
+
+
+
+
+ 留言内容
+
+
+
+
+ ip地址
+
+
+
+
+ 是否显示在前台,0否1是
+
+
+
+
+
+ 商户号
+
+
+
+
+ 柜台号
+
+
+
+
+ 分行号
+
+
+
+
+ 集团商户信息
+
+
+
+
+ 交易码
+
+
+
+
+ 商户类型
+
- 终端编号 1
-
+ 终端编号 1
+
+
+
+
+ 终端编号 2
+
+
+
+
+ 订单号
+
+
+
+
+ 码信息(一维码、二维码)
+
+
+
+
+ 订单金额,单位:元
+
+
+
+
+ 商品名称
+
+
+
+
+ 备注 1
+
+
+
+
+ 备注 2
+
+
+
+
+ 分账信息一
+
+
+
+
+ 分账信息二
+
+
+
+
+ 子商户公众账号 ID
+
+
+
+
+ 返回信息位图
+
+
+
+
+ 实名支付
+
+
+
+
+ 商品详情
+
+
+
+
+ 订单优惠标记
+
+
+
+
+ 公钥
+
+
+
+
+ 请求地址
+
+
+
+
+ 是否删除空值
+
+
+
+
+ 退款参数
+
+
+
+
+ 订单ID
+
+
+
+
+ 商品名称
+
+
+
+
+ 支付金额(小数点最多两位)
+
+
+
+
+ 二维码/条码信息
+
+
+
+
+ 备注信息1
+
+
+
+
+ 备注信息2
+
+
+
+
+ 订单参数
+
+
+
+
+ 订单号
+
+
+
+
+ 退款金额
+
+
+
+
+ 退款流水号(可选)
+
+
+
+
+ 退款返回消息
+
+
+
+
+ 序列号
+
+
+
+
+ 商户号
+
+
+
+
+ 交易码
+
+
+
+
+ 返回码
+
+
+
+
+ 返回码说明
+
+
+
+
+ 语言
+
+
+
+
+ 订单信息
+
+
+
+
+ 订单信息
+
+
+
+
+ 订单号
+
+
+
+
+ 支付金额
+
+
+
+
+ 退款金额
+
+
+
+
+ 备注1
+
+
+
+
+ 备注2
+
+
+
+
+ 退款返回结果消息
+
+
+
+
+ 订单号
+
+
+
+
+ 支付金额
+
+
+
+
+ 退款金额
+
+
+
+
+ 序列号
+
+
+
+
+ 商户号
+
+
+
+
+ 交易码
+
+
+
+
+ 返回码
+
+
+
+
+ 返回码说明
+
+
+
+
+ 语言
+
+
+
+
+ 支付结果dto
+
+
+
+
+ 支付结果
+ Y:成功
+ N:失败
+ U:不确定
+ Q:待轮询
+
+
+
+
+ 订单ID
+
+
+
+
+ 支付金额
+
+
+
+
+ 二维码类型
+ 1:龙支付
+ 2:微信
+ 3:支付宝
+ 4:银联
+
+
+
+
+ 等待时间-轮询等待时间
+
+
+
+
+ 全局事件跟踪号-建行交易流水号
+
+
+
+
+ 错误码
+
+
+
+
+ 错误信息
+
+
+
+
+ 验证签名-防止伪造攻击
+
+
+
+
+ 返回支付结果
+
+
+
+
+ 发起的订单ID
+
+
+
+
+ 返回支付的金额
+
+
+
+
+ 返回支付的类型 1:龙支付 2:微信 3:支付宝 4:银联
+
+
+
+
+ 返回建行的流水号
+
+
+
+
+ 错误代码
+
+
+
+
+ 错误信息
+
+
+
+
+ 实现IJob的类
+
+
+
+
+ 命名空间
+
+
+
+
+ 类名
+
+
+
+
+ 备注
+
+
+
+
+ 服务器VM
+
+
+
+
+ 环境变量
+
+
+
+
+ 系统架构
+
+
+
+
+ ContentRootPath
+
+
+
+
+ WebRootPath
+
+
+
+
+ .NET Core版本
+
+
+
+
+ 内存占用
+
+
+
+
+ 启动时间
+
+
+
+
+ 菜单展示model
+
+
+
+
+ 调度任务触发器信息实体
+
+
+
+
+ 任务ID
+
+
+
+
+ 任务名称
+
+
+
+
+ 任务分组
+
+
+
+
+ 触发器ID
+
+
+
+
+ 触发器名称
+
+
+
+
+ 触发器分组
+
+
+
+
+ 触发器状态
+
+
+
+
+ 用来测试 RestSharp Get 请求
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 用来测试 RestSharp Post 请求
+
+
+
+
+ 留言排名展示类
+
+
+
+ 博客ID
+
+
+
+
+
+ 评论数量
+
+
+
+ 博客标题
+
+
+
+
+
+ 微信接口消息DTO
+ 作者:胡丁文
+ 时间:2020-03-25
+
+
+
+
+ 微信公众号ID(数据库查询)
+
+
+
+
+ 错误代码
+
+
+
+
+ 错误信息
+
+
+
+
+ token
+
+
+
+
+ 过期时间(秒)
+
+
+
+
+ 用户关注数
+
+
+
+
+ 获取用户数量
+
+
+
+
+ 获取用户OpenIDs
+
+
+
+
+ 下一个关注用户
+
+
+
+
+ 微信消息模板列表
+
+
+
+
+ 微信菜单
+
+
+
+
+ 二维码票据
+
+
+
+
+ 二维码过期时间
+
+
+
+
+ 二维码地址
+
+
+
+
+ 关注状态
+
+
+
+
+ 用户微信ID
+
+
+
+
+ 昵称
+
+
+
+
+ 性别
+
+
+
+
+ 语言
+
+
+
+
+ 城市
+
+
+
+
+ 省份
+
+
+
+
+ 城市
+
+
+
+
+ 头像地址
+
-
+
- 终端编号 2
-
+ 微信推送消息Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:16:16
+
-
+
- 订单号
-
+ 推送关键信息
+
-
+
- 码信息(一维码、二维码)
-
+ 推送卡片消息Dto
+
-
+
- 订单金额,单位:元
-
+ 微信推送消息Dto
+ 作者:胡丁文
+ 时间:2020-11-23 16:29:05
+
-
+
- 商品名称
-
+ 推送关键信息
+
-
+
- 备注 1
-
+ 推送卡片消息Dto
+
-
+
- 备注 2
-
+ 消息模板dto(如何填写数据,请参考微信模板即可)
+ 作者:胡丁文
+ 时间:2020-4-1 09:32:16
+
-
+
- 分账信息一
-
+ 消息模板
+
-
+
- 分账信息二
-
+ 标题
+
-
+
- 子商户公众账号 ID
-
+ 标题颜色(颜色代码都必须为#开头的16进制代码)
+
-
+
- 返回信息位图
-
+ 内容1
+
-
+
- 实名支付
-
+ 内容1颜色
+
-
+
- 商品详情
-
+ 内容2
+
-
+
- 订单优惠标记
-
+ 内容2颜色
+
-
+
- 公钥
+ 内容3
-
+
- 请求地址
+ 内容3颜色
-
+
- 是否删除空值
+ 内容4
-
+
- 退款参数
+ 内容4颜色
-
+
- 订单ID
+ 内容5
-
+
- 商品名称
+ 内容5颜色
-
+
- 支付金额(小数点最多两位)
+ 备注信息
-
+
- 二维码/条码信息
+ 备注信息颜色
-
+
- 备注信息1
+ 跳转连接
-
+
- 备注信息2
+ 获取微信菜单DTO,用于存放具体菜单内容
-
+
- 订单参数
+ 获取微信菜单DTO
-
+
- 订单号
+ 按钮列表(最多三个)
-
+
- 退款金额
+ 微信OpenID列表Dto
-
+
- 退款流水号(可选)
+ 推送详细数据
+ 作者:胡丁文
+ 时间:2020-4-8 09:16:16
-
+
- 退款返回消息
+ 推送给微信所需Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:16:16
-
+
- 序列号
+ 推送微信用户ID
-
+
- 商户号
+ 推送的模板ID
-
+
- 交易码
+ 推送URL地址
-
+
- 返回码
+ 推送的数据
-
+
- 返回码说明
+ 微信keyword所需Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:18:08
-
+
- 语言
+ 内容
-
+
- 订单信息
+ 文字颜色
-
+
- 订单信息
+ 图文链接标题
-
+
- 订单号
+ 图文描述
-
+
- 支付金额
+ 访问URL
-
+
- 退款金额
+ 图片URL
-
+
- 备注1
+ 图片mediaID
-
+
- 备注2
+ 推送模拟消息Dto
+ 作者:胡丁文
+ 时间:2020-4-24 14:52:44
-
+
- 退款返回结果消息
+ 当前选中的微信公众号
-
+
- 订单号
+ 当前选中的操作集合
-
+
- 支付金额
+ 当前选中的绑定还是订阅
-
+
- 退款金额
+ 当前选中的微信客户
-
+
- 序列号
+ 当前选中的消息类型
-
+
- 商户号
+ 当前选中要发送的用户
-
+
- 交易码
+ 文本消息
-
+
- 返回码
+ 图片消息
-
+
- 返回码说明
+ 语音消息
-
+
- 语言
+ 视频消息
-
+
- 支付结果dto
+ 链接消息
-
+
- 支付结果
- Y:成功
- N:失败
- U:不确定
- Q:待轮询
+ 文字消息
-
+
- 订单ID
+ 视频标题
-
+
- 支付金额
+ 视频封面mediaID
-
+
- 二维码类型
- 1:龙支付
- 2:微信
- 3:支付宝
- 4:银联
+ 视频mediaID
-
+
- 等待时间-轮询等待时间
+ 语音mediaID
-
+
- 全局事件跟踪号-建行交易流水号
+ 微信二维码预装发送信息dto
-
+
- 错误码
+ 微信二维码预装具体消息
-
+
- 错误信息
+ 微信二维码预装信息DTO
-
+
- 验证签名-防止伪造攻击
+ 返回给调用者的Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:52:06
-
+
- 返回支付结果
+ 微信公众号ID
-
+
- 发起的订单ID
+ 公司代码
-
+
- 返回支付的金额
+ 数据
-
+
- 返回支付的类型 1:龙支付 2:微信 3:支付宝 4:银联
+ 微信消息模板Dto
-
+
- 返回建行的流水号
+ 微信推送所需信息(公司版本)
+ 作者:胡丁文
+ 时间:2020-4-8 09:04:36
-
+
- 错误代码
+ 微信公众号ID
-
+
- 错误信息
+ 公司代码
-
+
- 实现IJob的类
+ 用户id
-
+
- 命名空间
+ 用户昵称
-
+
- 类名
+ 微信推送所需信息(OpenID版本)
+ 作者:胡丁文
+ 时间:2020-11-23 16:27:29
-
+
- 备注
+ 微信公众号ID
-
+
- 服务器VM
+ 微信OpenID
-
+
- 环境变量
+ 微信验证Dto
+ 作者:胡丁文
+ 时间:2020-4-1 21:34:07
+
+
+
+
+ 微信公众号唯一标识
-
+
- 系统架构
+ 验证成功后返回给微信的字符串
-
+
- ContentRootPath
+ 签名
-
+
- WebRootPath
+ 时间戳
-
+
- .NET Core版本
+ 随机数
-
+
- 内存占用
+ 微信XmlDto
+ 作者:胡丁文
+ 时间:2020-4-3 20:31:26
+
+
+
+
+ 微信公众号唯一表示
-
+
- 启动时间
+ 微信开发者
+
+
+
+
+ 来自谁
+
+
+
+
+ 创建时间
-
+
- 菜单展示model
+ 消息类型
+
+
+
+
+ 文字内容
+
+
+
+
+ 消息ID
-
+
- 调度任务触发器信息实体
+ 消息事件
+
+
+
+
+ 事件key值
+
+
+
+
+ 图片地址
-
+
- 任务ID
+ 多媒体ID
-
+
- 任务名称
+ 格式
-
+
- 任务分组
+ 语音失败
-
+
- 触发器ID
+ 缩略媒体ID
-
+
- 触发器名称
+ 地理位置维度
-
+
- 触发器分组
+ 地理位置经度
-
+
- 触发器状态
+ 地图缩放大小
-
+
- 用来测试 RestSharp Get 请求
+ 地理位置信息
-
+
-
+ 消息标题
-
+
-
+ 消息描述
-
+
- 用来测试 RestSharp Post 请求
+ 消息链接
-
+
- 留言排名展示类
+ 二维码的ticket,可用来换取二维码图片
-
- 博客ID
-
+
+
+ 地理位置纬度
-
+
- 评论数量
+ 地理位置经度
-
- 博客标题
-
+
+
+ 地理位置精度
diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml
index 87df176e..0ed61c91 100644
--- a/Blog.Core.Api/Blog.Core.xml
+++ b/Blog.Core.Api/Blog.Core.xml
@@ -26,14 +26,14 @@
-
+
获取博客详情
-
+
获取详情【无权限】
@@ -67,7 +67,7 @@
-
+
删除博客
@@ -137,6 +137,12 @@
+
+
+ 权限数据库导出excel
+
+
+
健康检查
@@ -153,19 +159,17 @@
图片管理
-
+
下载图片(支持中文字符)
-
-
+
- 上传图片,多文件,可以使用 postman 测试,
- 如果是单文件,可以 参数写 IFormFile file1
+ 上传图片,多文件
-
+
@@ -173,7 +177,7 @@
登录管理【无权限】
-
+
构造函数注入
@@ -182,6 +186,7 @@
+
@@ -232,12 +237,25 @@
+
+
+ swagger登录
+
+
+
+
+
+
+ weixin登录
+
+
+
接口管理
-
+
获取全部接口api
@@ -259,13 +277,20 @@
-
+
删除一条接口
+
+
+ 导入多条接口信息
+
+
+
+
服务器配置信息
@@ -337,7 +362,7 @@
菜单管理
-
+
构造函数
@@ -345,19 +370,22 @@
+
+
-
+
获取菜单
+
-
+
查询树形 Table
@@ -379,7 +407,7 @@
-
+
获取菜单树
@@ -387,16 +415,23 @@
-
+
+
+ 获取路由树
+
+
+
+
+
获取路由树
-
+
- 通过角色获取菜单【无权限】
+ 通过角色获取菜单
@@ -408,13 +443,26 @@
-
+
删除菜单
+
+
+ 导入多条菜单信息
+
+
+
+
+
+
+ 系统接口菜单同步接口
+
+
+
角色管理
@@ -442,7 +490,7 @@
-
+
删除角色
@@ -471,42 +519,42 @@
-
+
删除一个任务
-
+
启动计划任务
-
+
停止一个计划任务
-
+
暂停一个计划任务
-
+
恢复一个计划任务
-
+
重启一个计划任务
@@ -519,13 +567,25 @@
-
+
立即执行任务
+
+
+ 获取任务运行日志
+
+
+
+
+
+ 任务概况
+
+
+
类别管理【无权限】
@@ -566,7 +626,7 @@
-
+
获取详情【无权限】
@@ -587,14 +647,14 @@
-
+
删除 bug
-
+
测试事务在AOP中的使用
@@ -606,15 +666,17 @@
用户管理
-
+
构造函数
-
+
+
+
@@ -633,21 +695,21 @@
令牌
-
+
添加一个用户
-
+
更新用户与角色
-
+
删除用户
@@ -659,12 +721,13 @@
用户角色关系
-
+
构造函数
+
@@ -682,7 +745,7 @@
-
+
新建用户角色关系
@@ -695,19 +758,23 @@
Values控制器
-
+
- ValuesController
+ 测试Rabbit消息队列发送
+
+
+
+
+ 测试Rabbit消息队列订阅
-
-
-
-
-
-
-
-
-
+
+
+
+ 测试SqlSugar二级缓存
+ 可设置过期时间
+ 或通过接口方式更新该数据,也会离开清除缓存
+
+
@@ -776,28 +843,17 @@
-
-
- 测试http请求 RestSharp Get
-
-
-
-
-
- 测试http请求 RestSharp Post
-
-
-
测试多库连接
-
+
- 测试http请求 WebApiClient Get
+ 测试Fulent做参数校验
+
@@ -823,6 +879,347 @@
通过此处的key格式为 xx:xx:x
+
+
+ 获取雪花Id
+
+
+
+
+
+ 测试缓存
+
+
+
+
+
+ 雪花Id To DateTime
+
+
+
+
+
+
+ WeChatCompanyController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ WeChatConfigController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ 微信公众号管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+ 更新Token
+
+
+
+
+
+
+ 刷新Token
+
+
+
+
+
+
+ 获取模板
+
+
+
+
+
+
+ 获取菜单
+
+
+
+
+
+
+ 更新菜单
+
+
+
+
+
+
+ 获取订阅用户(所有)
+
+
+
+
+
+
+ 入口
+
+
+
+
+
+
+ 获取订阅用户
+
+
+
+
+
+
+
+ 获取一个绑定员工公众号二维码
+
+ 消息
+
+
+
+
+ 推送卡片消息接口
+
+ 卡片消息对象
+
+
+
+
+ 推送卡片消息接口
+
+ 卡片消息对象
+
+
+
+
+ 推送文本消息
+
+ 消息对象
+
+
+
+
+ 通过绑定用户获取微信用户信息(一般用于初次绑定检测)
+
+ 信息
+
+
+
+
+ 用户解绑
+
+ 消息
+
+
+
+
+ WeChatPushLogController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ WeChatSubController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ 查询树形 Table
+
+ 父节点
+ 关键字
+
+
+
+
+ 获取部门树
+
+
+
+
服务管理
@@ -863,28 +1260,339 @@
-
+
- 自定义路由 /api/{version}/[controler]/[action]
+ SignalR测试
-
+
- 分组名称,是来实现接口 IApiDescriptionGroupNameProvider
+ 向指定用户发送消息
+
+
+
-
+
- 自定义路由构造函数,继承基类路由
+ 向指定角色发送消息
-
+
+
+
-
+
- 自定义版本+路由构造函数,继承基类路由
+ 分表demo
+
+
+
+
+ 分页获取数据
+
+
+
+
+
+
+
+
+
+
+ 根据ID获取信息
+
+
+
+
+
+
+ 添加一条测试数据
+
+
+
+
+
+
+ 修改一条测试数据
+
+
+
+
+
+
+ 根据id删除数据
+
+
+
+
+
+
+ SqlSugar 相关测试
+
+
+
+
+ SqlSugar 相关测试
+
+
+
+
+ 测试建表后,SqlSugar缓存
+
+
+
+
+
+ 缓存管理
+
+
+
+
+ 缓存管理
+
+
+
+
+ 获取全部缓存
+
+
+
+
+
+ 获取缓存
+
+
+
+
+
+ 新增
+
+
+
+
+
+ 删除全部缓存
+
+
+
+
+
+ 删除缓存
+
+
+
+
+
+ 数据库管理
+
+
+
+
+ 获取库配置
+
+
+
+
+
+ 获取表信息
+
+ 配置Id
+ 读取类型
+
+
+
+
+ 获取表字段
+
+ 表名
+ ConfigId
+ 读取类型
+
+
+
+
+ 编辑表备注
+
+
+
+
+
+ 编辑列备注
+
+
+
+
+
+ 动态建表 CURD
+
+
+
+
+ 动态type
+
+
+
+
+
+ 动态type 继承BaseEntity
+
+
+
+
+
+ 测试建表
+
+
+
+
+
+ 测试查询
+
+
+
+
+
+ 测试写入
+
+
+
+
+
+ 多租户-多库方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增数据
+
+
+
+
+
+ 多租户-Id方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增业务数据
+
+
+
+
+
+ 多租户-多表方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增数据
+
+
+
+
+
+ 租户管理
+
+
+
+
+ 获取全部租户
+
+
+
+
+
+ 获取租户信息
+
+
+
+
+
+ 新增租户信息
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 修改租户信息
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 删除租户
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 枚举测试
+
+
+
+
+ 获取学生信息
+
+ 学生类型
+
+
+ 学生信息
+
+
+
+ 学生类型
+
+
+
+
+ 小学生
+
+
+
+
+ 中学生
+
+
+
+
+ 大学生
+
+
+
+
+ 学生姓名
+
+
+
+
+ 学生年龄
+
+
+
+
+ 学生类型
-
-
@@ -927,11 +1635,28 @@
全局路由前缀公约
-
+
- 根据环境变量定向配置文件名称
+ 自定义路由 /api/{version}/[controler]/[action]
-
+
+
+
+ 分组名称,是来实现接口 IApiDescriptionGroupNameProvider
+
+
+
+
+ 自定义路由构造函数,继承基类路由
+
+
+
+
+
+ 自定义版本+路由构造函数,继承基类路由
+
+
+
diff --git a/Blog.Core.Api/Controllers/BaseApiController.cs b/Blog.Core.Api/Controllers/BaseApiController.cs
new file mode 100644
index 00000000..97a938ea
--- /dev/null
+++ b/Blog.Core.Api/Controllers/BaseApiController.cs
@@ -0,0 +1,88 @@
+using Blog.Core.Model;
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+
+namespace Blog.Core.Controllers
+{
+ public class BaseApiController : Controller
+ {
+ [NonAction]
+ public MessageModel Success(T data, string msg = "成功")
+ {
+ return new MessageModel()
+ {
+ success = true,
+ msg = msg,
+ response = data,
+ };
+ }
+
+ // [NonAction]
+ //public MessageModel Success(T data, string msg = "成功",bool success = true)
+ //{
+ // return new MessageModel()
+ // {
+ // success = success,
+ // msg = msg,
+ // response = data,
+ // };
+ //}
+ [NonAction]
+ public MessageModel Success(string msg = "成功")
+ {
+ return new MessageModel()
+ {
+ success = true,
+ msg = msg,
+ response = null,
+ };
+ }
+
+ [NonAction]
+ public MessageModel Failed(string msg = "失败", int status = 500)
+ {
+ return new MessageModel()
+ {
+ success = false,
+ status = status,
+ msg = msg,
+ response = null,
+ };
+ }
+
+ [NonAction]
+ public MessageModel Failed(string msg = "失败", int status = 500)
+ {
+ return new MessageModel()
+ {
+ success = false,
+ status = status,
+ msg = msg,
+ response = default,
+ };
+ }
+
+ [NonAction]
+ public MessageModel> SuccessPage(int page, int dataCount, int pageSize, List data,
+ int pageCount, string msg = "获取成功")
+ {
+ return new MessageModel>()
+ {
+ success = true,
+ msg = msg,
+ response = new PageModel(page, dataCount, pageSize, data)
+ };
+ }
+
+ [NonAction]
+ public MessageModel> SuccessPage(PageModel pageModel, string msg = "获取成功")
+ {
+ return new MessageModel>()
+ {
+ success = true,
+ msg = msg,
+ response = pageModel
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/BaseApiCpntroller.cs b/Blog.Core.Api/Controllers/BaseApiCpntroller.cs
deleted file mode 100644
index 92f9db16..00000000
--- a/Blog.Core.Api/Controllers/BaseApiCpntroller.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using Blog.Core.Model;
-using Microsoft.AspNetCore.Mvc;
-using System.Collections.Generic;
-
-namespace Blog.Core.Controllers
-{
- public class BaseApiCpntroller : Controller
- {
- [NonAction]
- public MessageModel Success(T data, string msg = "成功")
- {
- return new MessageModel()
- {
- success = true,
- msg = msg,
- response = data,
- };
- }
- [NonAction]
- public MessageModel Success(string msg = "成功")
- {
- return new MessageModel()
- {
- success = true,
- msg = msg,
- response = null,
- };
- }
- [NonAction]
- public MessageModel Failed(string msg = "失败", int status = 500)
- {
- return new MessageModel()
- {
- success = false,
- status = status,
- msg = msg,
- response = null,
- };
- }
- [NonAction]
- public MessageModel Failed(string msg = "失败", int status = 500)
- {
- return new MessageModel()
- {
- success = false,
- status = status,
- msg = msg,
- response = default,
- };
- }
- [NonAction]
- public MessageModel> SuccessPage(int page, int dataCount, List data, int pageCount, string msg = "获取成功")
- {
-
- return new MessageModel>()
- {
- success = true,
- msg = msg,
- response = new PageModel()
- {
- page = page,
- dataCount = dataCount,
- data = data,
- pageCount = pageCount,
- }
- };
- }
- [NonAction]
- public MessageModel> SuccessPage(PageModel pageModel, string msg = "获取成功")
- {
-
- return new MessageModel>()
- {
- success = true,
- msg = msg,
- response = new PageModel()
- {
- page = pageModel.page,
- dataCount = pageModel.dataCount,
- data = pageModel.data,
- pageCount = pageModel.pageCount,
- }
- };
- }
- }
-}
diff --git a/Blog.Core.Api/Controllers/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs
index 24860971..d0e9a235 100644
--- a/Blog.Core.Api/Controllers/BlogController.cs
+++ b/Blog.Core.Api/Controllers/BlogController.cs
@@ -1,8 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
+using System.Linq.Expressions;
using System.Text.RegularExpressions;
-using System.Threading.Tasks;
using Blog.Core.Common.Helper;
using Blog.Core.IServices;
using Blog.Core.Model;
@@ -11,7 +8,7 @@
using Blog.Core.SwaggerHelper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
+using Serilog;
using StackExchange.Profiling;
using static Blog.Core.Extensions.CustomApiVersion;
@@ -22,7 +19,7 @@ namespace Blog.Core.Controllers
///
[Produces("application/json")]
[Route("api/Blog")]
- public class BlogController : BaseApiCpntroller
+ public class BlogController : BaseApiController
{
public IBlogArticleServices _blogArticleServices { get; set; }
private readonly ILogger _logger;
@@ -87,7 +84,7 @@ public async Task>> Get(int id, int page = 1
[HttpGet("{id}")]
//[Authorize(Policy = "Scope_BlogModule_Policy")]
[Authorize]
- public async Task> Get(int id)
+ public async Task> Get(long id)
{
return Success(await _blogArticleServices.GetBlogDetails(id));
}
@@ -100,15 +97,16 @@ public async Task> Get(int id)
///
[HttpGet]
[Route("DetailNuxtNoPer")]
- public async Task> DetailNuxtNoPer(int id)
+ public async Task> DetailNuxtNoPer(long id)
{
_logger.LogInformation("xxxxxxxxxxxxxxxxxxx");
+ Log.Information("yyyyyyyyyyyyyyyyy");
return Success(await _blogArticleServices.GetBlogDetails(id));
}
[HttpGet]
[Route("GoUrl")]
- public async Task GoUrl(int id = 0)
+ public async Task GoUrl(long id = 0)
{
var response = await _blogArticleServices.QueryById(id);
if (response != null && response.bsubmitter.IsNotEmptyOrNull())
@@ -132,7 +130,7 @@ public async Task>> GetBlogsByTypesForMVP(string
{
if (types.IsNotEmptyOrNull())
{
- var blogs = await _blogArticleServices.Query(d => d.bcategory != null && types.Contains(d.bcategory) && d.IsDeleted == false);
+ var blogs = await _blogArticleServices.Query(d => d.bcategory != null && types.Contains(d.bcategory) && d.IsDeleted == false, d => d.bID, false);
return Success(blogs);
}
return Success(new List() { });
@@ -140,7 +138,7 @@ public async Task>> GetBlogsByTypesForMVP(string
[HttpGet]
[Route("GetBlogByIdForMVP")]
- public async Task> GetBlogByIdForMVP(int id = 0)
+ public async Task> GetBlogByIdForMVP(long id = 0)
{
if (id > 0)
{
@@ -233,7 +231,9 @@ public async Task> Put([FromBody] BlogArticle BlogArticle)
model.btraffic = BlogArticle.btraffic;
if (await _blogArticleServices.Update(model))
- Success(BlogArticle?.bID.ObjToString());
+ {
+ return Success(BlogArticle?.bID.ObjToString());
+ }
}
}
return Failed("更新失败");
@@ -249,11 +249,15 @@ public async Task> Put([FromBody] BlogArticle BlogArticle)
[HttpDelete]
[Authorize(Permissions.Name)]
[Route("Delete")]
- public async Task> Delete(int id)
+ public async Task> Delete(long id)
{
if (id > 0)
{
var blogArticle = await _blogArticleServices.QueryById(id);
+ if (blogArticle == null)
+ {
+ return Failed("查询无数据");
+ }
blogArticle.IsDeleted = true;
return await _blogArticleServices.Update(blogArticle) ? Success(blogArticle?.bID.ObjToString(), "删除成功") : Failed("删除失败");
}
diff --git a/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs
index cad64362..553dee75 100644
--- a/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs
+++ b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs
@@ -1,7 +1,7 @@
using Blog.Core.Common;
using Blog.Core.Common.DB;
+using Blog.Core.Common.Seed;
using Blog.Core.Model;
-using Blog.Core.Model.Seed;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
@@ -15,7 +15,7 @@ namespace Blog.Core.Controllers
//[Authorize(Permissions.Name)]
public class DbFirstController : ControllerBase
{
- private readonly SqlSugarClient _sqlSugarClient;
+ private readonly SqlSugarScope _sqlSugarClient;
private readonly IWebHostEnvironment Env;
///
@@ -23,7 +23,7 @@ public class DbFirstController : ControllerBase
///
public DbFirstController(ISqlSugarClient sqlSugarClient, IWebHostEnvironment env)
{
- _sqlSugarClient = sqlSugarClient as SqlSugarClient;
+ _sqlSugarClient = sqlSugarClient as SqlSugarScope;
Env = env;
}
@@ -36,19 +36,19 @@ public MessageModel GetFrameFiles()
{
var data = new MessageModel() { success = true, msg = "" };
data.response += @"file path is:C:\my-file\}";
- var isMuti = Appsettings.app(new string[] { "MutiDBEnabled" }).ObjToBool();
+ var isMuti = BaseDBConfig.IsMulti;
if (Env.IsDevelopment())
{
data.response += $"Controller层生成:{FrameSeed.CreateControllers(_sqlSugarClient)} || ";
- BaseDBConfig.MutiConnectionString.allDbs.ToList().ForEach(m =>
+ BaseDBConfig.ValidConfig.ForEach(m =>
{
- _sqlSugarClient.ChangeDatabase(m.ConnId.ToLower());
- data.response += $"库{m.ConnId}-Model层生成:{FrameSeed.CreateModels(_sqlSugarClient, m.ConnId, isMuti)} || ";
- data.response += $"库{m.ConnId}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, m.ConnId, isMuti)} || ";
- data.response += $"库{m.ConnId}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, m.ConnId, isMuti)} || ";
- data.response += $"库{m.ConnId}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, m.ConnId, isMuti)} || ";
- data.response += $"库{m.ConnId}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, m.ConnId, isMuti)} || ";
+ _sqlSugarClient.ChangeDatabase(m.ConfigId.ToString().ToLower());
+ data.response += $"库{m.ConfigId}-Model层生成:{FrameSeed.CreateModels(_sqlSugarClient, m.ConfigId.ToString(), isMuti)} || ";
+ data.response += $"库{m.ConfigId}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, m.ConfigId.ToString(), isMuti)} || ";
+ data.response += $"库{m.ConfigId}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, m.ConfigId.ToString(), isMuti)} || ";
+ data.response += $"库{m.ConfigId}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, m.ConfigId.ToString(), isMuti)} || ";
+ data.response += $"库{m.ConfigId}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, m.ConfigId.ToString(), isMuti)} || ";
});
// 切回主库
@@ -74,7 +74,7 @@ public MessageModel GetFrameFilesByTableNames([FromBody]string[] tableNa
{
ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
- var isMuti = Appsettings.app(new string[] { "MutiDBEnabled" }).ObjToBool();
+ var isMuti = BaseDBConfig.IsMulti;
var data = new MessageModel() { success = true, msg = "" };
if (Env.IsDevelopment())
{
@@ -102,7 +102,7 @@ public MessageModel GetFrameFilesByTableNamesForEntity([FromBody] string
{
ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
- var isMuti = Appsettings.app(new string[] { "MutiDBEnabled" }).ObjToBool();
+ var isMuti = BaseDBConfig.IsMulti;
var data = new MessageModel() { success = true, msg = "" };
if (Env.IsDevelopment())
{
@@ -112,7 +112,7 @@ public MessageModel GetFrameFilesByTableNamesForEntity([FromBody] string
{
data.success = false;
data.msg = "当前不处于开发模式,代码生成不可用!";
- }
+ }
return data;
}
///
@@ -126,7 +126,7 @@ public MessageModel GetFrameFilesByTableNamesForController([FromBody] st
{
ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
- var isMuti = Appsettings.app(new string[] { "MutiDBEnabled" }).ObjToBool();
+ var isMuti = BaseDBConfig.IsMulti;
var data = new MessageModel() { success = true, msg = "" };
if (Env.IsDevelopment())
{
@@ -151,7 +151,7 @@ public MessageModel GetAllFrameFilesByTableNames([FromBody]string[] tabl
{
ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
- var isMuti = Appsettings.app(new string[] { "MutiDBEnabled" }).ObjToBool();
+ var isMuti = BaseDBConfig.IsMulti;
var data = new MessageModel() { success = true, msg = "" };
if (Env.IsDevelopment())
{
diff --git a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs
index 730a9e8d..7865cc69 100644
--- a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs
+++ b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs
@@ -1,17 +1,20 @@
using Blog.Core.Common.Helper;
-using Blog.Core.IRepository.UnitOfWork;
using Blog.Core.IServices;
using Blog.Core.Model;
using Blog.Core.Model.Models;
+using Magicodes.ExporterAndImporter.Core;
+using Magicodes.ExporterAndImporter.Excel;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Blog.Core.Repository.UnitOfWorks;
namespace Blog.Core.Controllers
{
@@ -20,31 +23,38 @@ namespace Blog.Core.Controllers
//[Authorize(Permissions.Name)]
public class MigrateController : ControllerBase
{
- private readonly IUnitOfWork _unitOfWork;
+ private readonly IUnitOfWorkManage _unitOfWorkManage;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
private readonly IUserRoleServices _userRoleServices;
private readonly IRoleServices _roleServices;
private readonly IPermissionServices _permissionServices;
private readonly IModuleServices _moduleServices;
+ private readonly IDepartmentServices _departmentServices;
+ private readonly ISysUserInfoServices _sysUserInfoServices;
private readonly IWebHostEnvironment _env;
- public MigrateController(IUnitOfWork unitOfWork,
+ public MigrateController(IUnitOfWorkManage unitOfWorkManage,
IRoleModulePermissionServices roleModulePermissionServices,
IUserRoleServices userRoleServices,
IRoleServices roleServices,
IPermissionServices permissionServices,
IModuleServices moduleServices,
+ IDepartmentServices departmentServices,
+ ISysUserInfoServices sysUserInfoServices,
IWebHostEnvironment env)
{
- _unitOfWork = unitOfWork;
+ _unitOfWorkManage = unitOfWorkManage;
_roleModulePermissionServices = roleModulePermissionServices;
_userRoleServices = userRoleServices;
_roleServices = roleServices;
_permissionServices = permissionServices;
_moduleServices = moduleServices;
+ _departmentServices = departmentServices;
+ _sysUserInfoServices = sysUserInfoServices;
_env = env;
}
+
///
/// 获取权限部分Map数据(从库)
/// 迁移到新库(主库)
@@ -54,24 +64,41 @@ public MigrateController(IUnitOfWork unitOfWork,
public async Task> DataMigrateFromOld2New()
{
var data = new MessageModel() { success = true, msg = "" };
+ var filterPermissionId = 122;
if (_env.IsDevelopment())
{
try
{
- // 获取权限集合数据
+ var apiList = await _moduleServices.Query(d => d.IsDeleted == false);
+ var permissionsAllList = await _permissionServices.Query(d => d.IsDeleted == false);
+ var permissions = permissionsAllList.Where(d => d.Pid == 0).ToList();
var rmps = await _roleModulePermissionServices.GetRMPMaps();
+ List pms = new();
+
// 当然,你可以做个where查询
- //rmps = rmps.Where(d => d.ModuleId > 88).ToList();
+ rmps = rmps.Where(d => d.PermissionId >= filterPermissionId).ToList();
- // 开启事务,保证数据一致性
- _unitOfWork.BeginTran();
+ InitPermissionTree(permissions, permissionsAllList, apiList);
- var rid = 0;
- var pid = 0;
- var mid = 0;
- var rpmid = 0;
+ var actionPermissionIds = permissionsAllList.Where(d => d.Id >= filterPermissionId).Select(d => d.Id).ToList();
+ List filterPermissionIds = new();
+ FilterPermissionTree(permissionsAllList, actionPermissionIds, filterPermissionIds);
+ permissions = permissions.Where(d => filterPermissionIds.Contains(d.Id)).ToList();
+
+ // 开启事务,保证数据一致性
+ _unitOfWorkManage.BeginTran();
// 注意信息的完整性,不要重复添加,确保主库没有要添加的数据
+
+ // 1、保持菜单和接口
+ await SavePermissionTreeAsync(permissions, pms);
+
+ long rid = 0;
+ long pid = 0;
+ long mid = 0;
+ long rpmid = 0;
+
+ // 2、保存关系表
foreach (var item in rmps)
{
// 角色信息,防止重复添加,做了判断
@@ -89,22 +116,10 @@ public async Task> DataMigrateFromOld2New()
}
}
- // 菜单
- if (item.Permission != null)
- {
- pid = await _permissionServices.Add(item.Permission);
- Console.WriteLine($"Permission Added:{item.Permission.Name}");
- }
-
- // 接口
- if (item.Module != null)
- {
- mid = await _moduleServices.Add(item.Module);
- Console.WriteLine($"Module Added:{item.Module.LinkUrl}");
- }
-
+ pid = (pms.FirstOrDefault(d => d.PidOld == item.PermissionId)?.PidNew).ObjToLong();
+ mid = (pms.FirstOrDefault(d => d.MidOld == item.ModuleId)?.MidNew).ObjToLong();
// 关系
- if (rid > 0 && pid > 0 && mid > 0)
+ if (rid > 0 && pid > 0)
{
rpmid = await _roleModulePermissionServices.Add(new RoleModulePermission()
{
@@ -121,14 +136,14 @@ public async Task> DataMigrateFromOld2New()
}
- _unitOfWork.CommitTran();
+ _unitOfWorkManage.CommitTran();
data.success = true;
data.msg = "导入成功!";
}
catch (Exception)
{
- _unitOfWork.RollbackTran();
+ _unitOfWorkManage.RollbackTran();
}
}
@@ -152,28 +167,97 @@ public async Task> SaveData2TsvAsync()
var data = new MessageModel() { success = true, msg = "" };
if (_env.IsDevelopment())
{
-
+
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
// 取出数据,序列化,自己可以处理判空
+ var SysUserInfoJson = JsonConvert.SerializeObject(await _sysUserInfoServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "SysUserInfo.tsv"), SysUserInfoJson, Encoding.UTF8);
+
+ var DepartmentJson = JsonConvert.SerializeObject(await _departmentServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Department.tsv"), DepartmentJson, Encoding.UTF8);
+
var rolesJson = JsonConvert.SerializeObject(await _roleServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
- FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Role_New.tsv"), rolesJson, Encoding.UTF8);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Role.tsv"), rolesJson, Encoding.UTF8);
+
+ var UserRoleJson = JsonConvert.SerializeObject(await _userRoleServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "UserRole.tsv"), UserRoleJson, Encoding.UTF8);
var permissionsJson = JsonConvert.SerializeObject(await _permissionServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
- FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Permission_New.tsv"), permissionsJson, Encoding.UTF8);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Permission.tsv"), permissionsJson, Encoding.UTF8);
var modulesJson = JsonConvert.SerializeObject(await _moduleServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
- FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Modules_New.tsv"), modulesJson, Encoding.UTF8);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Modules.tsv"), modulesJson, Encoding.UTF8);
var rmpsJson = JsonConvert.SerializeObject(await _roleModulePermissionServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
- FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "RoleModulePermission_New.tsv"), rmpsJson, Encoding.UTF8);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "RoleModulePermission.tsv"), rmpsJson, Encoding.UTF8);
+
+
+
+ data.success = true;
+ data.msg = "生成成功!";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+
+
+ ///
+ /// 权限数据库导出excel
+ ///
+ ///
+ [HttpGet]
+ public async Task> SaveData2ExcelAsync()
+ {
+ var data = new MessageModel() { success = true, msg = "" };
+ if (_env.IsDevelopment())
+ {
+
+ JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
+ {
+ DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
+ };
+
+ // 取出数据,序列化,自己可以处理判空
+ IExporter exporter = new ExcelExporter();
+ var SysUserInfoList = await _sysUserInfoServices.Query(d => d.IsDeleted == false);
+ var result = await exporter.ExportAsByteArray(SysUserInfoList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "SysUserInfo.xlsx"), result);
+
+ var DepartmentList = await _departmentServices.Query(d => d.IsDeleted == false);
+ var DepartmentResult = await exporter.ExportAsByteArray(DepartmentList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "Department.xlsx"), DepartmentResult);
+
+ var RoleList = await _roleServices.Query(d => d.IsDeleted == false);
+ var RoleResult = await exporter.ExportAsByteArray(RoleList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), RoleResult);
+ var UserRoleList = await _userRoleServices.Query(d => d.IsDeleted == false);
+ var UserRoleResult = await exporter.ExportAsByteArray(UserRoleList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "UserRole.xlsx"), UserRoleResult);
+
+ var PermissionList = await _permissionServices.Query(d => d.IsDeleted == false);
+ var PermissionResult = await exporter.ExportAsByteArray(PermissionList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "Permission.xlsx"), PermissionResult);
+
+ var ModulesList = await _moduleServices.Query(d => d.IsDeleted == false);
+ var ModulesResult = await exporter.ExportAsByteArray(ModulesList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "Modules.xlsx"), ModulesResult);
+
+ var RoleModulePermissionList = await _roleModulePermissionServices.Query(d => d.IsDeleted == false);
+ var RoleModulePermissionResult = await exporter.ExportAsByteArray(RoleModulePermissionList);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.excel", "RoleModulePermission.xlsx"), RoleModulePermissionResult);
data.success = true;
@@ -188,5 +272,88 @@ public async Task> SaveData2TsvAsync()
return data;
}
+ private void InitPermissionTree(List permissionsTree, List all, List apis)
+ {
+ foreach (var item in permissionsTree)
+ {
+ item.Children = all.Where(d => d.Pid == item.Id).ToList();
+ item.Module = apis.FirstOrDefault(d => d.Id == item.Mid);
+ InitPermissionTree(item.Children, all, apis);
+ }
+ }
+
+ private void FilterPermissionTree(List permissionsAll, List actionPermissionId, List filterPermissionIds)
+ {
+ actionPermissionId = actionPermissionId.Distinct().ToList();
+ var doneIds = permissionsAll.Where(d => actionPermissionId.Contains(d.Id) && d.Pid == 0).Select(d => d.Id).ToList();
+ filterPermissionIds.AddRange(doneIds);
+
+ var hasDoIds = permissionsAll.Where(d => actionPermissionId.Contains(d.Id) && d.Pid != 0).Select(d => d.Pid).ToList();
+ if (hasDoIds.Any())
+ {
+ FilterPermissionTree(permissionsAll, hasDoIds, filterPermissionIds);
+ }
+ }
+
+ private async Task SavePermissionTreeAsync(List permissionsTree, List pms, long permissionId = 0)
+ {
+ var parendId = permissionId;
+
+ foreach (var item in permissionsTree)
+ {
+ PM pm = new PM();
+ // 保留原始主键id
+ pm.PidOld = item.Id;
+ pm.MidOld = (item.Module?.Id).ObjToLong();
+
+ long mid = 0;
+ // 接口
+ if (item.Module != null)
+ {
+ var moduleModel = (await _moduleServices.Query(d => d.LinkUrl == item.Module.LinkUrl)).FirstOrDefault();
+ if (moduleModel != null)
+ {
+ mid = moduleModel.Id;
+ }
+ else
+ {
+ mid = await _moduleServices.Add(item.Module);
+ }
+ pm.MidNew = mid;
+ Console.WriteLine($"Moudle Added:{item.Module.Name}");
+ }
+ // 菜单
+ if (item != null)
+ {
+ var permissionModel = (await _permissionServices.Query(d => d.Name == item.Name && d.Pid == item.Pid && d.Mid == item.Mid)).FirstOrDefault();
+ item.Pid = parendId;
+ item.Mid = mid;
+ if (permissionModel != null)
+ {
+ permissionId = permissionModel.Id;
+ }
+ else
+ {
+ permissionId = await _permissionServices.Add(item);
+ }
+
+ pm.PidNew = permissionId;
+ Console.WriteLine($"Permission Added:{item.Name}");
+ }
+ pms.Add(pm);
+
+ await SavePermissionTreeAsync(item.Children, pms, permissionId);
+ }
+ }
+
+
+ }
+
+ public class PM
+ {
+ public long PidOld { get; set; }
+ public long MidOld { get; set; }
+ public long PidNew { get; set; }
+ public long MidNew { get; set; }
}
}
diff --git a/Blog.Core.Api/Controllers/DepartmentController.cs b/Blog.Core.Api/Controllers/DepartmentController.cs
new file mode 100644
index 00000000..faf1f850
--- /dev/null
+++ b/Blog.Core.Api/Controllers/DepartmentController.cs
@@ -0,0 +1,215 @@
+using Blog.Core.Common.Helper;
+using Blog.Core.Controllers;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+using System.Linq.Expressions;
+using System.Text;
+
+namespace Blog.Core.Api.Controllers
+{
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ [Authorize(Permissions.Name)]
+ public class DepartmentController : BaseApiController
+ {
+ private readonly IDepartmentServices _departmentServices;
+ private readonly IWebHostEnvironment _env;
+
+ public DepartmentController(IDepartmentServices departmentServices, IWebHostEnvironment env)
+ {
+ _departmentServices = departmentServices;
+ _env = env;
+ }
+
+ [HttpGet]
+ public async Task>> Get(int page = 1, string key = "", int intPageSize = 50)
+ {
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ Expression> whereExpression = a => true;
+
+ return new MessageModel>()
+ {
+ msg = "获取成功",
+ success = true,
+ response = await _departmentServices.QueryPage(whereExpression, page, intPageSize)
+ };
+
+ }
+
+ [HttpGet("{id}")]
+ public async Task> Get(string id)
+ {
+ return new MessageModel()
+ {
+ msg = "获取成功",
+ success = true,
+ response = await _departmentServices.QueryById(id)
+ };
+ }
+
+ ///
+ /// 查询树形 Table
+ ///
+ /// 父节点
+ /// 关键字
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task>> GetTreeTable(long f = 0, string key = "")
+ {
+ List departments = new List();
+ var departmentList = await _departmentServices.Query(d => d.IsDeleted == false);
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ if (key != "")
+ {
+ departments = departmentList.Where(a => a.Name.Contains(key)).OrderBy(a => a.OrderSort).ToList();
+ }
+ else
+ {
+ departments = departmentList.Where(a => a.Pid == f).OrderBy(a => a.OrderSort).ToList();
+ }
+
+ foreach (var item in departments)
+ {
+ List pidarr = new() { };
+ var parent = departmentList.FirstOrDefault(d => d.Id == item.Pid);
+
+ while (parent != null)
+ {
+ pidarr.Add(parent.Id);
+ parent = departmentList.FirstOrDefault(d => d.Id == parent.Pid);
+ }
+
+ pidarr.Reverse();
+ pidarr.Insert(0, 0);
+ item.PidArr = pidarr;
+
+ item.hasChildren = departmentList.Where(d => d.Pid == item.Id).Any();
+ }
+
+
+ return Success(departments, "获取成功");
+ }
+
+ ///
+ /// 获取部门树
+ ///
+ ///
+ ///
+ [HttpGet]
+ public async Task> GetDepartmentTree(long pid = 0)
+ {
+ var departments = await _departmentServices.Query(d => d.IsDeleted == false);
+ var departmentTrees = (from child in departments
+ where child.IsDeleted == false
+ orderby child.Id
+ select new DepartmentTree
+ {
+ value = child.Id,
+ label = child.Name,
+ Pid = child.Pid,
+ order = child.OrderSort,
+ }).ToList();
+ DepartmentTree rootRoot = new DepartmentTree
+ {
+ value = 0,
+ Pid = 0,
+ label = "根节点"
+ };
+
+ departmentTrees = departmentTrees.OrderBy(d => d.order).ToList();
+
+
+ RecursionHelper.LoopToAppendChildren(departmentTrees, rootRoot, pid);
+
+ return Success(rootRoot, "获取成功");
+ }
+
+ [HttpPost]
+ public async Task> Post([FromBody] Department request)
+ {
+ var data = new MessageModel();
+
+ var id = await _departmentServices.Add(request);
+ data.success = id > 0;
+ if (data.success)
+ {
+ data.response = id.ObjToString();
+ data.msg = "添加成功";
+ }
+
+ return data;
+ }
+
+ [HttpPut]
+ public async Task> Put([FromBody] Department request)
+ {
+ var data = new MessageModel();
+ data.success = await _departmentServices.Update(request);
+ if (data.success)
+ {
+ data.msg = "更新成功";
+ data.response = request?.Id.ObjToString();
+ }
+
+ return data;
+ }
+
+ [HttpDelete]
+ public async Task> Delete(long id)
+ {
+ var data = new MessageModel();
+ var model = await _departmentServices.QueryById(id);
+ model.IsDeleted = true;
+ data.success = await _departmentServices.Update(model);
+ if (data.success)
+ {
+ data.msg = "删除成功";
+ data.response = model?.Id.ObjToString();
+ }
+
+
+ return data;
+ }
+
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task> SaveData2Tsv()
+ {
+ var data = new MessageModel() { success = true, msg = "" };
+ if (_env.IsDevelopment())
+ {
+
+ JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
+ {
+ DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
+ };
+
+ var rolesJson = JsonConvert.SerializeObject(await _departmentServices.Query(d => d.IsDeleted == false), microsoftDateFormatSettings);
+ FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Department_New.tsv"), rolesJson, Encoding.UTF8);
+
+ data.success = true;
+ data.msg = "生成成功!";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/ImgController.cs b/Blog.Core.Api/Controllers/ImgController.cs
index 716db28e..5ba85388 100644
--- a/Blog.Core.Api/Controllers/ImgController.cs
+++ b/Blog.Core.Api/Controllers/ImgController.cs
@@ -1,11 +1,6 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Blog.Core.Model;
+using Blog.Core.Model;
+using Blog.Core.Model.ViewModels;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Blog.Core.Controllers
@@ -16,20 +11,28 @@ namespace Blog.Core.Controllers
[Route("api/[controller]")]
[ApiController]
[Authorize]
- public class ImgController : Controller
+ public class ImgController : BaseApiController
{
+
+ private readonly IWebHostEnvironment _env;
+
+ public ImgController(IWebHostEnvironment webHostEnvironment)
+ {
+ _env = webHostEnvironment;
+ }
+
+
// GET: api/Download
///
/// 下载图片(支持中文字符)
///
- ///
///
[HttpGet]
[Route("/images/Down/Pic")]
- public FileStreamResult DownImg([FromServices] IWebHostEnvironment environment)
+ public FileStreamResult DownImg()
{
string foldername = "";
- string filepath = Path.Combine(environment.WebRootPath, foldername, "测试下载中文名称的图片.png");
+ string filepath = Path.Combine(_env.WebRootPath, foldername, "测试下载中文名称的图片.png");
var stream = System.IO.File.OpenRead(filepath);
string fileExt = ".jpg"; // 这里可以写一个获取文件扩展名的方法,获取扩展名
//获取文件的ContentType
@@ -42,71 +45,50 @@ public FileStreamResult DownImg([FromServices] IWebHostEnvironment environment)
}
///
- /// 上传图片,多文件,可以使用 postman 测试,
- /// 如果是单文件,可以 参数写 IFormFile file1
+ /// 上传图片,多文件
///
- ///
+ ///
///
[HttpPost]
[Route("/images/Upload/Pic")]
- public async Task> InsertPicture([FromServices] IWebHostEnvironment environment)
+ public async Task> InsertPicture([FromForm]UploadFileDto dto)
{
- var data = new MessageModel();
- string path = string.Empty;
- string foldername = "images";
- IFormFileCollection files = null;
-
-
- // 获取提交的文件
- files = Request.Form.Files;
- // 获取附带的数据
- var max_ver = Request.Form["max_ver"].ObjToString();
-
-
- if (files == null || !files.Any()) { data.msg = "请选择上传的文件。"; return data; }
+
+ if (dto.file == null || !dto.file.Any()) return Failed("请选择上传的文件。");
//格式限制
var allowType = new string[] { "image/jpg", "image/png", "image/jpeg" };
+
+ var allowedFile = dto.file.Where(c => allowType.Contains(c.ContentType));
+ if (!allowedFile.Any()) return Failed("图片格式错误");
+ if (allowedFile.Sum(c => c.Length) > 1024 * 1024 * 4) return Failed("图片过大");
- string folderpath = Path.Combine(environment.WebRootPath, foldername);
+ string foldername = "images";
+ string folderpath = Path.Combine(_env.WebRootPath, foldername);
if (!Directory.Exists(folderpath))
{
Directory.CreateDirectory(folderpath);
}
-
- if (files.Any(c => allowType.Contains(c.ContentType)))
+ foreach (var file in allowedFile)
{
- if (files.Sum(c => c.Length) <= 1024 * 1024 * 4)
- {
- //foreach (var file in files)
- var file = files.FirstOrDefault();
- string strpath = Path.Combine(foldername, DateTime.Now.ToString("MMddHHmmss") + Path.GetFileName(file.FileName));
- path = Path.Combine(environment.WebRootPath, strpath);
-
- using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
- {
- await file.CopyToAsync(stream);
- }
-
- data = new MessageModel()
- {
- response = strpath,
- msg = "上传成功",
- success = true,
- };
- return data;
- }
- else
+ string strpath = Path.Combine(foldername, DateTime.Now.ToString("MMddHHmmss") + Path.GetFileName(file.FileName));
+ var path = Path.Combine(_env.WebRootPath, strpath);
+
+ using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
- data.msg = "图片过大";
- return data;
+ await file.CopyToAsync(stream);
}
}
- else
+ var excludeFiles = dto.file.Except(allowedFile);
+
+ if (excludeFiles.Any())
{
- data.msg = "图片格式错误";
- return data;
+ var infoMsg = $"{string.Join('、', excludeFiles.Select(c => c.FileName))} 图片格式错误";
+ return Success(null, infoMsg);
}
+
+ return Success(null, "上传成功");
+
}
@@ -114,14 +96,14 @@ public async Task> InsertPicture([FromServices] IWebHostEnv
[HttpGet]
[Route("/images/Down/Bmd")]
[AllowAnonymous]
- public FileStreamResult DownBmd([FromServices] IWebHostEnvironment environment, string filename)
+ public FileStreamResult DownBmd(string filename)
{
if (string.IsNullOrEmpty(filename))
{
return null;
}
// 前端 blob 接收,具体查看前端admin代码
- string filepath = Path.Combine(environment.WebRootPath, Path.GetFileName(filename));
+ string filepath = Path.Combine(_env.WebRootPath, Path.GetFileName(filename));
if (System.IO.File.Exists(filepath))
{
var stream = System.IO.File.OpenRead(filepath);
diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs
index e007cc6d..5b40773d 100644
--- a/Blog.Core.Api/Controllers/LoginController.cs
+++ b/Blog.Core.Api/Controllers/LoginController.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.IdentityModel.Tokens.Jwt;
-using System.Linq;
-using System.Security.Claims;
-using System.Threading.Tasks;
-using Blog.Core.AuthHelper;
+using Blog.Core.AuthHelper;
using Blog.Core.AuthHelper.OverWrite;
using Blog.Core.Common.Helper;
using Blog.Core.IServices;
@@ -12,9 +6,11 @@
using Blog.Core.Model.ViewModels;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Blog.Core.Common.Swagger;
+
namespace Blog.Core.Controllers
{
@@ -24,14 +20,14 @@ namespace Blog.Core.Controllers
[Produces("application/json")]
[Route("api/Login")]
[AllowAnonymous]
- public class LoginController : BaseApiCpntroller
+ public class LoginController : BaseApiController
{
readonly ISysUserInfoServices _sysUserInfoServices;
readonly IUserRoleServices _userRoleServices;
readonly IRoleServices _roleServices;
readonly PermissionRequirement _requirement;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
-
+ private readonly ILogger _logger;
///
/// 构造函数注入
@@ -41,17 +37,22 @@ public class LoginController : BaseApiCpntroller
///
///
///
- public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices, IRoleServices roleServices, PermissionRequirement requirement, IRoleModulePermissionServices roleModulePermissionServices)
+ ///
+ public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices,
+ IRoleServices roleServices, PermissionRequirement requirement,
+ IRoleModulePermissionServices roleModulePermissionServices, ILogger logger)
{
this._sysUserInfoServices = sysUserInfoServices;
this._userRoleServices = userRoleServices;
this._roleServices = roleServices;
_requirement = requirement;
_roleModulePermissionServices = roleModulePermissionServices;
+ _logger = logger;
}
#region 获取token的第1种方法
+
///
/// 获取JWT的方法1
///
@@ -62,16 +63,14 @@ public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServic
[Route("Token")]
public async Task> GetJwtStr(string name, string pass)
{
-
string jwtStr = string.Empty;
bool suc = false;
- //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
-
+ //这里就是用户登录以后,通过数据库去调取数据,分配权限的操作
+
var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass));
if (user != null)
{
-
- TokenModelJwt tokenModel = new TokenModelJwt { Uid = 1, Role = user };
+ TokenModelJwt tokenModel = new TokenModelJwt {Uid = 1, Role = user};
jwtStr = JwtHelper.IssueJwt(tokenModel);
suc = true;
@@ -102,7 +101,7 @@ public MessageModel GetJwtStrForNuxt(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
- //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
+ //这里就是用户登录以后,通过数据库去调取数据,分配权限的操作
//这里直接写死了
if (name == "admins" && pass == "admins")
{
@@ -119,9 +118,10 @@ public MessageModel GetJwtStrForNuxt(string name, string pass)
{
jwtStr = "login fail!!!";
}
+
var result = new
{
- data = new { success = suc, token = jwtStr }
+ data = new {success = suc, token = jwtStr}
};
return new MessageModel()
@@ -131,8 +131,8 @@ public MessageModel GetJwtStrForNuxt(string name, string pass)
response = jwtStr
};
}
- #endregion
+ #endregion
///
@@ -144,6 +144,7 @@ public MessageModel GetJwtStrForNuxt(string name, string pass)
[HttpGet]
[Route("JWTToken3.0")]
public async Task> GetJwtToken3(string name = "", string pass = "")
+
{
string jwtStr = string.Empty;
@@ -152,15 +153,21 @@ public async Task> GetJwtToken3(string name = "
pass = MD5Helper.MD5Encrypt32(pass);
- var user = await _sysUserInfoServices.Query(d => d.uLoginName == name && d.uLoginPWD == pass && d.tdIsDelete == false);
+ var user = await _sysUserInfoServices.Query(d =>
+ d.LoginName == name && d.LoginPWD == pass && d.IsDeleted == false);
if (user.Count > 0)
{
var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
- var claims = new List {
+ var claims = new List
+ {
new Claim(ClaimTypes.Name, name),
- new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().uID.ToString()),
- new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) };
+ new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()),
+ new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()),
+ new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.DateToTimeStamp()),
+ new Claim(ClaimTypes.Expiration,
+ DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
+ };
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
@@ -190,6 +197,14 @@ orderby item.Id
}
}
+ [HttpGet]
+ [Route("GetJwtTokenSecret")]
+ public async Task> GetJwtTokenSecret(string name = "", string pass = "")
+ {
+ var rlt = await GetJwtToken3(name, pass);
+ return rlt;
+ }
+
///
/// 请求刷新Token(以旧换新)
///
@@ -204,17 +219,27 @@ public async Task> RefreshToken(string token =
if (string.IsNullOrEmpty(token))
return Failed("token无效,请重新登录!");
var tokenModel = JwtHelper.SerializeJwt(token);
- if (tokenModel != null && tokenModel.Uid > 0)
+ if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0)
{
var user = await _sysUserInfoServices.QueryById(tokenModel.Uid);
- if (user != null)
+ var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
+ if (value != null && user.CriticalModifyTime > value.ObjToDate())
{
- var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.uLoginName, user.uLoginPWD);
+ return Failed("很抱歉,授权已失效,请重新授权!");
+ }
+
+ if (user != null && !(value != null && user.CriticalModifyTime > value.ObjToDate()))
+ {
+ var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
- var claims = new List {
- new Claim(ClaimTypes.Name, user.uLoginName),
- new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()),
- new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) };
+ var claims = new List
+ {
+ new Claim(ClaimTypes.Name, user.LoginName),
+ new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()),
+ new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.DateToTimeStamp()),
+ new Claim(ClaimTypes.Expiration,
+ DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
+ };
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//用户标识
@@ -225,6 +250,7 @@ public async Task> RefreshToken(string token =
return Success(refreshToken, "获取成功");
}
}
+
return Failed("认证失败!");
}
@@ -239,7 +265,8 @@ public async Task> RefreshToken(string token =
///
[HttpGet]
[Route("jsonp")]
- public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, int expiresAbsoulute = 30)
+ public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30,
+ int expiresAbsoulute = 30)
{
TokenModelJwt tokenModel = new TokenModelJwt
{
@@ -266,5 +293,54 @@ public string Md5Password(string password = "")
{
return MD5Helper.MD5Encrypt32(password);
}
+
+ ///
+ /// swagger登录
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("/api/Login/swgLogin")]
+ public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest)
+ {
+ if (loginRequest is null)
+ {
+ return new {result = false};
+ }
+
+ try
+ {
+ var result = await GetJwtToken3(loginRequest.name, loginRequest.pwd);
+ if (result.success)
+ {
+ HttpContext.SuccessSwagger();
+ HttpContext.SuccessSwaggerJwt(result.response.token);
+ return new {result = true};
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Swagger登录异常");
+ }
+
+ return new {result = false};
+ }
+
+ ///
+ /// weixin登录
+ ///
+ ///
+ [HttpGet]
+ [Route("wxLogin")]
+ public dynamic WxLogin(string g = "", string token = "")
+ {
+ return new {g, token};
+ }
+ }
+
+ public class SwaggerLoginRequest
+ {
+ public string name { get; set; }
+ public string pwd { get; set; }
}
}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/ModuleController.cs b/Blog.Core.Api/Controllers/ModuleController.cs
index a7ad5e58..3b286808 100644
--- a/Blog.Core.Api/Controllers/ModuleController.cs
+++ b/Blog.Core.Api/Controllers/ModuleController.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Linq.Expressions;
-using System.Threading.Tasks;
+using System.Linq.Expressions;
using Blog.Core.Common.HttpContextUser;
using Blog.Core.IServices;
using Blog.Core.Model;
@@ -16,12 +14,12 @@ namespace Blog.Core.Controllers
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize(Permissions.Name)]
- public class ModuleController : ControllerBase
+ public class ModuleController : BaseApiController
{
readonly IModuleServices _moduleServices;
readonly IUser _user;
-
+
public ModuleController(IModuleServices moduleServices, IUser user)
{
_moduleServices = moduleServices;
@@ -36,24 +34,30 @@ public ModuleController(IModuleServices moduleServices, IUser user)
///
// GET: api/User
[HttpGet]
- public async Task>> Get(int page = 1, string key = "")
+ public async Task>> Get(int page = 1, string key = "", int pageSize = 50)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
{
key = "";
}
- int intPageSize = 50;
- Expression> whereExpression = a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key));
+ Expression> whereExpression = a => a.IsDeleted != true && ((a.Name != null && a.Name.Contains(key) || (a.LinkUrl != null && a.LinkUrl.Contains(key))));
- var data = await _moduleServices.QueryPage(whereExpression, page, intPageSize, " Id desc ");
+ PageModel data = new PageModel();
- return new MessageModel>()
+ if (page == -1)
+ {
+ var modules = await _moduleServices.Query(whereExpression, " Id desc ");
+ data.data = modules;
+ }
+ else
{
- msg = "获取成功",
- success = data.dataCount >= 0,
- response = data
- };
+ data = await _moduleServices.QueryPage(whereExpression, page, pageSize, " Id desc ");
+ }
+
+
+ return Success(data, "获取成功");
+
}
@@ -73,20 +77,11 @@ public string Get(string id)
[HttpPost]
public async Task> Post([FromBody] Modules module)
{
- var data = new MessageModel();
-
module.CreateId = _user.ID;
module.CreateBy = _user.Name;
-
var id = (await _moduleServices.Add(module));
- data.success = id > 0;
- if (data.success)
- {
- data.response = id.ObjToString();
- data.msg = "添加成功";
- }
+ return id > 0 ? Success(id.ObjToString(), "添加成功") : Failed();
- return data;
}
///
@@ -98,18 +93,22 @@ public async Task> Post([FromBody] Modules module)
[HttpPut]
public async Task> Put([FromBody] Modules module)
{
- var data = new MessageModel();
- if (module != null && module.Id > 0)
- {
- data.success = await _moduleServices.Update(module);
- if (data.success)
- {
- data.msg = "更新成功";
- data.response = module?.Id.ObjToString();
- }
- }
-
- return data;
+ //var data = new MessageModel();
+ //if (module != null && module.Id > 0)
+ //{
+ //data.success = await _moduleServices.Update(module);
+ //if (data.success)
+ //{
+ // data.msg = "更新成功";
+ // data.response = module?.Id.ObjToString();
+ //}
+
+ // }
+
+ //return data;
+ if (module == null || module.Id <= 0)
+ return Failed("缺少参数");
+ return await _moduleServices.Update(module) ? Success(module?.Id.ObjToString(), "更新成功") : Failed();
}
///
@@ -119,22 +118,56 @@ public async Task> Put([FromBody] Modules module)
///
// DELETE: api/ApiWithActions/5
[HttpDelete]
- public async Task> Delete(int id)
+ public async Task> Delete(long id)
{
- var data = new MessageModel();
- if (id > 0)
+ if (id <= 0)
+ return Failed("缺少参数");
+ var userDetail = await _moduleServices.QueryById(id);
+ if (userDetail == null)
+ return Failed("信息不存在");
+
+ userDetail.IsDeleted = true;
+ return await _moduleServices.Update(userDetail) ? Success(userDetail?.Id.ObjToString(), "删除成功") : Failed("删除失败");
+
+ //var data = new MessageModel();
+ //if (id > 0)
+ //{
+ // var userDetail = await _moduleServices.QueryById(id);
+ // userDetail.IsDeleted = true;
+ // data.success = await _moduleServices.Update(userDetail);
+ // if (data.success)
+ // {
+ // data.msg = "删除成功";
+ // data.response = userDetail?.Id.ObjToString();
+ // }
+ //}
+ //return data;
+ }
+
+ ///
+ /// 导入多条接口信息
+ ///
+ ///
+ ///
+ // POST: api/User
+ [HttpPost]
+ public async Task> BatchPost([FromBody] List modules)
+ {
+ string ids = string.Empty;
+ int sucCount = 0;
+
+ for (int i = 0; i < modules.Count; i++)
{
- var userDetail = await _moduleServices.QueryById(id);
- userDetail.IsDeleted = true;
- data.success = await _moduleServices.Update(userDetail);
- if (data.success)
+ var module = modules[i];
+ if (module != null)
{
- data.msg = "删除成功";
- data.response = userDetail?.Id.ObjToString();
+ module.CreateId = _user.ID;
+ module.CreateBy = _user.Name;
+ ids += (await _moduleServices.Add(module));
+ sucCount++;
}
}
-
- return data;
+ return ids.IsNotEmptyOrNull() ? Success(ids, $"{sucCount}条数据添加成功") : Failed();
}
}
}
diff --git a/Blog.Core.Api/Controllers/MonitorController.cs b/Blog.Core.Api/Controllers/MonitorController.cs
index 28ff3265..d707d497 100644
--- a/Blog.Core.Api/Controllers/MonitorController.cs
+++ b/Blog.Core.Api/Controllers/MonitorController.cs
@@ -3,42 +3,36 @@
using Blog.Core.Common.LogHelper;
using Blog.Core.Hubs;
using Blog.Core.IServices;
-using Blog.Core.Middlewares;
using Blog.Core.Model;
using Blog.Core.Model.ViewModels;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
-using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
-using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
-using System.Threading.Tasks;
+using Blog.Core.Extensions.Middlewares;
namespace Blog.Core.Controllers
{
[Route("api/[Controller]/[action]")]
[ApiController]
[AllowAnonymous]
- public class MonitorController : BaseApiCpntroller
+ public class MonitorController : BaseApiController
{
private readonly IHubContext _hubContext;
private readonly IWebHostEnvironment _env;
private readonly IApplicationUserServices _applicationUserServices;
private readonly ILogger _logger;
- public MonitorController(IHubContext hubContext, IWebHostEnvironment env, IApplicationUserServices applicationUserServices, ILogger logger)
+ public MonitorController(IHubContext hubContext, IWebHostEnvironment env,
+ IApplicationUserServices applicationUserServices, ILogger logger)
{
- _hubContext = hubContext;
- _env = env;
+ _hubContext = hubContext;
+ _env = env;
_applicationUserServices = applicationUserServices;
- _logger = logger;
+ _logger = logger;
}
///
@@ -50,14 +44,14 @@ public MessageModel Server()
{
return Success(new ServerViewModel()
{
- EnvironmentName = _env.EnvironmentName,
- OSArchitecture = RuntimeInformation.OSArchitecture.ObjToString(),
- ContentRootPath = _env.ContentRootPath,
- WebRootPath = _env.WebRootPath,
+ EnvironmentName = _env.EnvironmentName,
+ OSArchitecture = RuntimeInformation.OSArchitecture.ObjToString(),
+ ContentRootPath = _env.ContentRootPath,
+ WebRootPath = _env.WebRootPath,
FrameworkDescription = RuntimeInformation.FrameworkDescription,
- MemoryFootprint = (Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB",
- WorkingTime = DateHelper.TimeSubTract(DateTime.Now, Process.GetCurrentProcess().StartTime)
- });
+ MemoryFootprint = (Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB",
+ WorkingTime = DateHelper.TimeSubTract(DateTime.Now, Process.GetCurrentProcess().StartTime)
+ }, "获取服务器配置信息成功");
}
@@ -69,71 +63,114 @@ public MessageModel Server()
[HttpGet]
public MessageModel> Get()
{
+ if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool())
+ {
+ _hubContext.Clients.All.SendAsync("ReceiveUpdate", "执行成功").Wait();
+ }
- _hubContext.Clients.All.SendAsync("ReceiveUpdate", LogLock.GetLogData()).Wait();
-
- return Success>(null);
+ return Success>(null, "执行成功");
}
-
[HttpGet]
public MessageModel GetRequestApiinfoByWeek()
{
- return Success(LogLock.RequestApiinfoByWeek());
+ //后续补充扩展Log
+ return Success(new RequestApiWeekView(), "成功");
}
[HttpGet]
public MessageModel GetAccessApiByDate()
{
- return new MessageModel()
- {
- msg = "获取成功",
- success = true,
- response = LogLock.AccessApiByDate()
- };
+ //return new MessageModel()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = LogLock.AccessApiByDate()
+ //};
+
+ //后续补充扩展Log
+ return Success(new AccessApiDateView(), "获取成功");
}
[HttpGet]
public MessageModel GetAccessApiByHour()
{
- return new MessageModel()
- {
- msg = "获取成功",
- success = true,
- response = LogLock.AccessApiByHour()
- };
+ //return new MessageModel()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = LogLock.AccessApiByHour()
+ //};
+
+ //后续补充扩展Log
+ return Success(new AccessApiDateView(), "获取成功");
}
private List GetAccessLogsToday(IWebHostEnvironment environment)
{
List userAccessModels = new();
- var accessLogs = LogLock.ReadLog(
- Path.Combine(environment.ContentRootPath, "Log"), "RecordAccessLogs_", Encoding.UTF8, ReadType.PrefixLatest
- ).ObjToString();
- try
- {
- return JsonConvert.DeserializeObject>("[" + accessLogs + "]");
- }
- catch (Exception)
- {
- var accLogArr = accessLogs.Split("\n");
- foreach (var item in accLogArr)
- {
- if (item.ObjToString() != "")
- {
- try
- {
- var accItem = JsonConvert.DeserializeObject(item.TrimEnd(','));
- userAccessModels.Add(accItem);
- }
- catch (Exception)
- {
- }
- }
- }
+ //后续补充扩展Log
+ // var accessLogs = LogLock.ReadLog(
+ // Path.Combine(environment.ContentRootPath, "Log"), "RecordAccessLogs_", Encoding.UTF8,
+ // ReadType.PrefixLatest
+ // ).ObjToString();
+ // try
+ // {
+ // return JsonConvert.DeserializeObject>("[" + accessLogs + "]");
+ // }
+ // catch (Exception)
+ // {
+ // var accLogArr = accessLogs.Split("\n");
+ // foreach (var item in accLogArr)
+ // {
+ // if (item.ObjToString() != "")
+ // {
+ // try
+ // {
+ // var accItem = JsonConvert.DeserializeObject(item.TrimEnd(','));
+ // userAccessModels.Add(accItem);
+ // }
+ // catch (Exception)
+ // {
+ // }
+ // }
+ // }
+ // }
- }
+ return userAccessModels;
+ }
+
+ private List GetAccessLogsTrend(IWebHostEnvironment environment)
+ {
+ List userAccessModels = new();
+ //后续补充扩展Log
+ // var accessLogs = LogLock.ReadLog(
+ // Path.Combine(environment.ContentRootPath, "Log"), "ACCESSTRENDLOG_", Encoding.UTF8,
+ // ReadType.PrefixLatest
+ // ).ObjToString();
+ // try
+ // {
+ // return JsonConvert.DeserializeObject>(accessLogs);
+ // }
+ // catch (Exception)
+ // {
+ // var accLogArr = accessLogs.Split("\n");
+ // foreach (var item in accLogArr)
+ // {
+ // if (item.ObjToString() != "")
+ // {
+ // try
+ // {
+ // var accItem = JsonConvert.DeserializeObject(item.TrimStart('[').TrimEnd(']'));
+ // userAccessModels.Add(accItem);
+ // }
+ // catch (Exception)
+ // {
+ // }
+ // }
+ // }
+ // }
return userAccessModels;
}
@@ -145,33 +182,42 @@ public MessageModel GetActiveUsers([FromServices] IWebHostEnvir
var Logs = accessLogsToday.OrderByDescending(d => d.BeginTime).Take(50).ToList();
- var errorCountToday = LogLock.GetLogData().Where(d => d.Import == 9).Count();
-
accessLogsToday = accessLogsToday.Where(d => d.User != "").ToList();
var activeUsers = (from n in accessLogsToday
- group n by new { n.User } into g
- select new ActiveUserVM
- {
- user = g.Key.User,
- count = g.Count(),
- }).ToList();
+ group n by new { n.User }
+ into g
+ select new ActiveUserVM
+ {
+ user = g.Key.User,
+ count = g.Count(),
+ }).ToList();
int activeUsersCount = activeUsers.Count;
activeUsers = activeUsers.OrderByDescending(d => d.count).Take(10).ToList();
- return new MessageModel()
+ //return new MessageModel()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = new WelcomeInitData()
+ // {
+ // activeUsers = activeUsers,
+ // activeUserCount = activeUsersCount,
+ // errorCount = errorCountToday,
+ // logs = Logs,
+ // activeCount = GetAccessLogsTrend(environment)
+ // }
+ //};
+
+ return Success(new WelcomeInitData()
{
- msg = "获取成功",
- success = true,
- response = new WelcomeInitData()
- {
- activeUsers = activeUsers,
- activeUserCount = activeUsersCount,
- errorCount = errorCountToday,
- logs = Logs
- }
- };
+ activeUsers = activeUsers,
+ activeUserCount = activeUsersCount,
+ errorCount = default,
+ logs = Logs,
+ activeCount = GetAccessLogsTrend(environment)
+ }, "获取成功");
}
[HttpGet]
@@ -179,17 +225,18 @@ public async Task> GetIds4Users()
{
List apiDates = new List