diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..3729ff0c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
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/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..7711e946
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# IDE0005: Using 指令是不需要的。
+dotnet_diagnostic.IDE0005.severity = warning
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..7c3b4241
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: http://apk.neters.club/laozhangisphigood.jpg
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..dd84ea78
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..96dd2c8f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,22 @@
+blank_issues_enabled: false
+contact_links:
+ - name: 🚨 Bug report | Bug 提交
+ url: https://github.com/anjoy8/Blog.Core/issues/new
+ about: |
+ Please report bugs here.
+ 请在此提交 Bug。
+ - name: 🙋 Feature request | 新功能提案
+ url: https://github.com/anjoy8/Blog.Core/issues/new
+ about: |
+ Please request features here.
+ 请在此提交新功能提案。
+ - name: 🤔 Consulting from the Blog.Core team | 咨询 作者
+ url: https://github.com/anjoy8/Blog.Core/issues/new
+ about: |
+ Get technical support, project audits, app deployments, and custom development from the core Blog.Core team.
+ 咨询核心 Blog.Core 团队以获得技术支持,项目审核,应用程序部署以及自定义开发等方面上的帮助。
+ - name: ❗️ All other issues | 其他问题
+ url: https://github.com/anjoy8/Blog.Core/issues/new
+ about: |
+ Please create all other issues here.
+ 请在此创建其他类型问题。
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..bbcbbe7d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
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
new file mode 100644
index 00000000..f11fee1f
--- /dev/null
+++ b/.github/workflows/dotnetcore.yml
@@ -0,0 +1,23 @@
+name: .NET Core
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ 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 86e9fc81..99804f89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@
[Rr]eleases/
x64/
x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
@@ -33,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.*
@@ -209,7 +214,7 @@ _pkginfo.txt
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
-!*.[Cc]ache/
+!?*.[Cc]ache/
# Others
ClientBin/
@@ -229,6 +234,8 @@ orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
+# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
+**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
@@ -333,5 +340,23 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+
# wwwroot/images
-*images/
\ No newline at end of file
+*images/
+.1YourProject
+.PublishFiles
+!.template.config/*.nupkg
+!Blog.Core.Webapi.Template.*.nupkg
+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/.template.config/Blog.Core.Webapi.Template.2.5.2.nupkg b/.template.config/Blog.Core.Webapi.Template.2.5.2.nupkg
new file mode 100644
index 00000000..1e04c646
Binary files /dev/null and b/.template.config/Blog.Core.Webapi.Template.2.5.2.nupkg differ
diff --git a/.template.config/template.json b/.template.config/template.json
new file mode 100644
index 00000000..3e074d94
--- /dev/null
+++ b/.template.config/template.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "lao zhang",
+ "classifications": [ "Web/WebAPI" ],
+ "name": "Blog.Core Dotnet",
+ "identity": "Blog.Core.Template",
+ "shortName": "blogcoretpl",
+ "tags": {
+ "language": "C#" ,
+ "type":"project"
+ },
+ "sourceName": "Blog.Core",
+ "preferNameDirectory": true
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..8bc327ab
--- /dev/null
+++ b/Blog.Core.Api/Blog.Core.Api.csproj
@@ -0,0 +1,122 @@
+
+
+
+ Exe
+ enable
+
+ Linux
+ true
+ default
+
+
+
+ ..\Blog.Core.Api\Blog.Core.xml
+ 1701;1702;1591
+
+
+
+ ..\Blog.Core\Blog.Core.xml
+ 1701;1702;1591
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ PreserveNewest
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml
new file mode 100644
index 00000000..00b4c5be
--- /dev/null
+++ b/Blog.Core.Api/Blog.Core.Model.xml
@@ -0,0 +1,3407 @@
+
+
+
+ Blog.Core.Model
+
+
+
+
+ 无任何权限
+
+
+
+
+ 自定义权限
+
+
+
+
+ 本部门
+
+
+
+
+ 本部门及以下
+
+
+
+
+ 仅自己
+
+
+
+
+ 所有
+
+
+
+
+ 以下model 来自ids4项目,多库模式,为了调取ids4数据
+ 角色表
+
+
+
+
+ 排序
+
+
+
+
+ 是否激活
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 以下model 来自ids4项目,多库模式,为了调取ids4数据
+ 用户表
+
+
+
+
+ 这是爱
+
+
+
+
+ id
+
+
+
+
+ 姓名
+
+
+
+
+ 年龄
+
+
+
+
+ 通用返回信息类
+
+
+
+
+ 状态码
+
+
+
+
+ 操作是否成功
+
+
+
+
+ 返回信息
+
+
+
+
+ 开发者信息
+
+
+
+
+ 返回数据集合
+
+
+
+
+ 返回成功
+
+ 消息
+
+
+
+
+ 返回成功
+
+ 消息
+ 数据
+
+
+
+
+ 返回失败
+
+ 消息
+
+
+
+
+ 返回失败
+
+ 消息
+ 数据
+
+
+
+
+ 返回消息
+
+ 失败/成功
+ 消息
+ 数据
+
+
+
+
+ 状态码
+
+
+
+
+ 操作是否成功
+
+
+
+
+ 返回信息
+
+
+
+
+ 返回数据集合
+
+
+
+
+ 用户访问趋势日志
+
+
+
+
+ 用户
+
+
+
+
+ 次数
+
+
+
+
+ 更新时间
+
+
+
+
+ 广告图片
+
+
+
+
+ 广告标题
+
+
+
+
+ 广告链接
+
+
+
+
+ 备注
+
+
+
+
+ 创建时间
+
+
+
+
+ 博客文章
+
+
+
+
+ 主键
+
+ 这里之所以没用RootEntity,是想保持和之前的数据库一致,主键是bID,不是Id
+
+
+
+ 创建人
+
+
+
+
+ 标题blog
+
+
+
+
+ 类别
+
+
+
+
+ 内容
+
+
+
+
+ 访问量
+
+
+
+
+ 评论数量
+
+
+
+
+ 修改时间
+
+
+
+
+ 创建时间
+
+
+
+
+ 备注
+
+
+
+
+ 逻辑删除
+
+
+
+
+ 评论
+
+
+
+
+ 博客文章 评论
+
+
+
+
+ 部门表
+
+
+
+
+ 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
+
+
+
+
+ 创建时间
+
+
+
+
+ 手机
+
+
+
+
+ qq
+
+
+
+
+ 留言内容
+
+
+
+
+ ip地址
+
+
+
+
+ 是否显示在前台,0否1是
+
+
+
+
+
+ 接口API地址信息表
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 名称
+
+
+
+
+ 菜单链接地址
+
+
+
+
+ 区域名称
+
+
+
+
+ 控制器名称
+
+
+
+
+ Action名称
+
+
+
+
+ 图标
+
+
+
+
+ 菜单编号
+
+
+
+
+ 排序
+
+
+
+
+ /描述
+
+
+
+
+ 是否是右侧菜单
+
+
+
+
+ 是否激活
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 日志记录
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 区域名
+
+
+
+
+ 区域控制器名
+
+
+
+
+ Action名称
+
+
+
+
+ IP地址
+
+
+
+
+ 描述
+
+
+
+
+ 登录时间
+
+
+
+
+ 登录名称
+
+
+
+
+ 用户ID
+
+
+
+
+ 密码库表
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 路由菜单表
+
+
+
+
+ 菜单执行Action名
+
+
+
+
+ 菜单显示名(如用户页、编辑(按钮)、删除(按钮))
+
+
+
+
+ 是否是按钮
+
+
+
+
+ 是否是隐藏菜单
+
+
+
+
+ 是否keepAlive
+
+
+
+
+ 按钮事件
+
+
+
+
+ 排序
+
+
+
+
+ 菜单图标
+
+
+
+
+ 菜单图标新
+
+
+
+
+ 菜单描述
+
+
+
+
+ 激活状态
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 角色表
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 角色名
+
+
+
+
+ 描述
+
+
+
+
+ 排序
+
+
+
+
+ 自定义权限的部门ids
+
+
+
+
+ 权限范围
+ -1 无任何权限;1 自定义权限;2 本部门;3 本部门及以下;4 仅自己;9 全部;
+
+
+
+
+ 是否激活
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 按钮跟权限关联表
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 状态
+ 中立字段,某些表可使用某些表不使用
+
+
+
+
+ 中立字段,某些表可使用某些表不使用
+ 逻辑上的删除,非物理删除
+ 例如:单据删除并非直接删除
+
+
+
+
+ 中立字段
+ 是否内置数据
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 更新者
+
+
+
+
+ 修改日期
+
+
+
+
+ 数据版本
+
+
+
+
+ 软删除 过滤器
+
+
+
+
+ 系统租户表
+ 根据TenantType 分为两种方案:
+ 1.按租户字段区分
+ 2.按租户分库
+
+
+
+ 注意:
+ 使用租户Id方案,无需配置分库的连接
+
+
+
+
+ 名称
+
+
+
+
+ 租户类型
+
+
+
+
+ 数据库/租户标识 不可重复
+ 使用Id方案,可无需配置
+
+
+
+
+ 主机
+ 使用Id方案,可无需配置
+
+
+
+
+ 数据库类型
+ 使用Id方案,可无需配置
+
+
+
+
+ 数据库连接
+ 使用Id方案,可无需配置
+
+
+
+
+ 状态
+
+
+
+
+ 备注
+
+
+
+
+ 用户信息表
+
+
+
+
+ 登录账号
+
+
+
+
+ 登录密码
+
+
+
+
+ 真实姓名
+
+
+
+
+ 状态
+
+
+
+
+ 部门
+
+
+
+
+ 备注
+
+
+
+
+ 创建时间
+
+
+
+
+ 更新时间
+
+
+
+
+ 关键业务修改时间
+
+
+
+
+ 最后异常时间
+
+
+
+
+ 错误次数
+
+
+
+
+ 登录账号
+
+
+
+
+ 租户Id
+
+
+
+
+ 任务日志表
+
+
+
+
+ 任务ID
+
+
+
+
+ 任务耗时
+
+
+
+
+ 执行结果(0-失败 1-成功)
+
+
+
+
+ 运行时间
+
+
+
+
+ 结束时间
+
+
+
+
+ 执行参数
+
+
+
+
+ 异常信息
+
+
+
+
+ 异常堆栈
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+ 任务名称
+
+
+
+
+ 任务分组
+
+
+
+
+ 任务计划表
+
+
+
+
+ 任务名称
+
+
+
+
+ 任务分组
+
+
+
+
+ 任务运行时间表达式
+
+
+
+
+ 任务所在DLL对应的程序集名称
+
+
+
+
+ 任务所在类
+
+
+
+
+ 任务描述
+
+
+
+
+ 执行次数
+
+
+
+
+ 开始时间
+
+
+
+
+ 结束时间
+
+
+
+
+ 触发器类型(0、simple 1、cron)
+
+
+
+
+ 执行间隔时间, 秒为单位
+
+
+
+
+ 循环执行次数
+
+
+
+
+ 已循环次数
+
+
+
+
+ 是否启动
+
+
+
+
+ 执行传参
+
+
+
+
+ 创建时间
+
+
+
+
+ 任务内存中的状态
+
+
+
+
+ 业务数据
+ 多租户 (Id 隔离)
+
+
+
+
+ 无需手动赋值
+
+
+
+
+ 名称
+
+
+
+
+ 金额
+
+
+
+
+ 多租户-多表方案 业务表 子表
+
+
+
+
+ 多租户-多表方案 业务表
+
+
+
+
+ 名称
+
+
+
+
+ 金额
+
+
+
+
+ 多租户-多库方案 业务表
+ 公共库无需标记[MultiTenant]特性
+
+
+
+
+ 名称
+
+
+
+
+ 金额
+
+
+
+
+ Tibug 类别
+
+
+
+
+ Tibug 博文
+
+
+
+
+ 用户跟角色关联表
+
+
+
+
+ 获取或设置是否禁用,逻辑上的删除,非物理删除
+
+
+
+
+ 创建ID
+
+
+
+
+ 创建者
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改ID
+
+
+
+
+ 修改者
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 公司ID
+
+
+
+
+ 公司名称
+
+
+
+
+ 公司IP
+
+
+
+
+ 公司备注
+
+
+
+
+ api地址
+
+
+
+
+ 是否激活
+
+
+
+
+ 创建者id
+
+
+
+
+ 创建人
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改者id
+
+
+
+
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 微信公众号唯一标识
+
+
+
+
+ 微信公众号名称
+
+
+
+
+ 微信账号
+
+
+
+
+ 微信名称
+
+
+
+
+ 应用ID
+
+
+
+
+ 应用秘钥
+
+
+
+
+ 公众号推送token
+
+
+
+
+ 验证秘钥(验证消息是否真实)
+
+
+
+
+ 微信公众号token过期时间
+
+
+
+
+ 备注
+
+
+
+
+ 是否激活
+
+
+
+
+ 创建者id
+
+
+
+
+ 创建人
+
+
+
+
+ 创建时间
+
+
+
+
+ 修改者id
+
+
+
+
+ 修改人
+
+
+
+
+ 修改时间
+
+
+
+
+
+
+
+
+
+ 推送ID
+
+
+
+
+ 来自谁
+
+
+
+
+ 推送IP
+
+
+
+
+ 推送客户
+
+
+
+
+ 推送用户
+
+
+
+
+ 推送模板ID
+
+
+
+
+ 推送内容
+
+
+
+
+ 推送时间
+
+
+
+
+ 推送状态(Y/N)
+
+
+
+
+ 备注
+
+
+
+
+ 推送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
+
+
+
+
+ 终端编号 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
+
+
+
+
+ 昵称
+
+
+
+
+ 性别
+
+
+
+
+ 语言
+
+
+
+
+ 城市
+
+
+
+
+ 省份
+
+
+
+
+ 城市
+
+
+
+
+ 头像地址
+
+
+
+
+ 微信推送消息Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:16:16
+
+
+
+
+ 推送关键信息
+
+
+
+
+ 推送卡片消息Dto
+
+
+
+
+ 微信推送消息Dto
+ 作者:胡丁文
+ 时间:2020-11-23 16:29:05
+
+
+
+
+ 推送关键信息
+
+
+
+
+ 推送卡片消息Dto
+
+
+
+
+ 消息模板dto(如何填写数据,请参考微信模板即可)
+ 作者:胡丁文
+ 时间:2020-4-1 09:32:16
+
+
+
+
+ 消息模板
+
+
+
+
+ 标题
+
+
+
+
+ 标题颜色(颜色代码都必须为#开头的16进制代码)
+
+
+
+
+ 内容1
+
+
+
+
+ 内容1颜色
+
+
+
+
+ 内容2
+
+
+
+
+ 内容2颜色
+
+
+
+
+ 内容3
+
+
+
+
+ 内容3颜色
+
+
+
+
+ 内容4
+
+
+
+
+ 内容4颜色
+
+
+
+
+ 内容5
+
+
+
+
+ 内容5颜色
+
+
+
+
+ 备注信息
+
+
+
+
+ 备注信息颜色
+
+
+
+
+ 跳转连接
+
+
+
+
+ 获取微信菜单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
+
+
+
+
+ 图片mediaID
+
+
+
+
+ 推送模拟消息Dto
+ 作者:胡丁文
+ 时间:2020-4-24 14:52:44
+
+
+
+
+ 当前选中的微信公众号
+
+
+
+
+ 当前选中的操作集合
+
+
+
+
+ 当前选中的绑定还是订阅
+
+
+
+
+ 当前选中的微信客户
+
+
+
+
+ 当前选中的消息类型
+
+
+
+
+ 当前选中要发送的用户
+
+
+
+
+ 文本消息
+
+
+
+
+ 图片消息
+
+
+
+
+ 语音消息
+
+
+
+
+ 视频消息
+
+
+
+
+ 链接消息
+
+
+
+
+ 文字消息
+
+
+
+
+ 视频标题
+
+
+
+
+ 视频封面mediaID
+
+
+
+
+ 视频mediaID
+
+
+
+
+ 语音mediaID
+
+
+
+
+ 微信二维码预装发送信息dto
+
+
+
+
+ 微信二维码预装具体消息
+
+
+
+
+ 微信二维码预装信息DTO
+
+
+
+
+ 返回给调用者的Dto
+ 作者:胡丁文
+ 时间:2020-4-8 09:52:06
+
+
+
+
+ 微信公众号ID
+
+
+
+
+ 公司代码
+
+
+
+
+ 数据
+
+
+
+
+ 微信消息模板Dto
+
+
+
+
+ 微信推送所需信息(公司版本)
+ 作者:胡丁文
+ 时间:2020-4-8 09:04:36
+
+
+
+
+ 微信公众号ID
+
+
+
+
+ 公司代码
+
+
+
+
+ 用户id
+
+
+
+
+ 用户昵称
+
+
+
+
+ 微信推送所需信息(OpenID版本)
+ 作者:胡丁文
+ 时间:2020-11-23 16:27:29
+
+
+
+
+ 微信公众号ID
+
+
+
+
+ 微信OpenID
+
+
+
+
+ 微信验证Dto
+ 作者:胡丁文
+ 时间:2020-4-1 21:34:07
+
+
+
+
+ 微信公众号唯一标识
+
+
+
+
+ 验证成功后返回给微信的字符串
+
+
+
+
+ 签名
+
+
+
+
+ 时间戳
+
+
+
+
+ 随机数
+
+
+
+
+ 微信XmlDto
+ 作者:胡丁文
+ 时间:2020-4-3 20:31:26
+
+
+
+
+ 微信公众号唯一表示
+
+
+
+
+ 微信开发者
+
+
+
+
+ 来自谁
+
+
+
+
+ 创建时间
+
+
+
+
+ 消息类型
+
+
+
+
+ 文字内容
+
+
+
+
+ 消息ID
+
+
+
+
+ 消息事件
+
+
+
+
+ 事件key值
+
+
+
+
+ 图片地址
+
+
+
+
+ 多媒体ID
+
+
+
+
+ 格式
+
+
+
+
+ 语音失败
+
+
+
+
+ 缩略媒体ID
+
+
+
+
+ 地理位置维度
+
+
+
+
+ 地理位置经度
+
+
+
+
+ 地图缩放大小
+
+
+
+
+ 地理位置信息
+
+
+
+
+ 消息标题
+
+
+
+
+ 消息描述
+
+
+
+
+ 消息链接
+
+
+
+
+ 二维码的ticket,可用来换取二维码图片
+
+
+
+
+ 地理位置纬度
+
+
+
+
+ 地理位置经度
+
+
+
+
+ 地理位置精度
+
+
+
+
diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml
new file mode 100644
index 00000000..0ed61c91
--- /dev/null
+++ b/Blog.Core.Api/Blog.Core.xml
@@ -0,0 +1,1662 @@
+
+
+
+ Blog.Core.Api
+
+
+
+
+ 博客管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+ 获取博客列表【无权限】
+
+
+
+
+
+
+
+
+
+ 获取博客详情
+
+
+
+
+
+
+ 获取详情【无权限】
+
+
+
+
+
+
+ 获取博客测试信息 v2版本
+
+
+
+
+
+ 添加博客【无权限】
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 更新博客信息
+
+
+
+
+
+
+ 删除博客
+
+
+
+
+
+
+ apache jemeter 压力测试
+ 更新接口
+
+
+
+
+
+ 构造函数
+
+
+
+
+ 获取 整体框架 文件(主库)(一般可用第一次生成)
+
+
+
+
+
+ 获取仓储层和服务层(需指定表名和数据库)
+
+ 数据库链接名称
+ 需要生成的表名
+
+
+
+
+ 获取实体(需指定表名和数据库)
+
+ 数据库链接名称
+ 需要生成的表名
+
+
+
+
+ 获取控制器(需指定表名和数据库)
+
+ 数据库链接名称
+ 需要生成的表名
+
+
+
+
+ DbFrist 根据数据库表名 生成整体框架,包含Model层(一般可用第一次生成)
+
+ 数据库链接名称
+ 需要生成的表名
+
+
+
+
+ 获取权限部分Map数据(从库)
+ 迁移到新库(主库)
+
+
+
+
+
+ 权限数据库导出tsv
+
+
+
+
+
+ 权限数据库导出excel
+
+
+
+
+
+ 健康检查
+
+
+
+
+ 健康检查接口
+
+
+
+
+
+ 图片管理
+
+
+
+
+ 下载图片(支持中文字符)
+
+
+
+
+
+ 上传图片,多文件
+
+
+
+
+
+
+ 登录管理【无权限】
+
+
+
+
+ 构造函数注入
+
+
+
+
+
+
+
+
+
+
+ 获取JWT的方法1
+
+
+
+
+
+
+
+ 获取JWT的方法2:给Nuxt提供
+
+
+
+
+
+
+
+ 获取JWT的方法3:整个系统主要方法
+
+
+
+
+
+
+
+ 请求刷新Token(以旧换新)
+
+
+
+
+
+
+ 获取JWT的方法4:给 JSONP 测试
+
+
+
+
+
+
+
+
+
+
+ 测试 MD5 加密字符串
+
+
+
+
+
+
+ swagger登录
+
+
+
+
+
+
+ weixin登录
+
+
+
+
+
+ 接口管理
+
+
+
+
+ 获取全部接口api
+
+
+
+
+
+
+
+ 添加一条接口信息
+
+
+
+
+
+
+ 更新接口信息
+
+
+
+
+
+
+ 删除一条接口
+
+
+
+
+
+
+ 导入多条接口信息
+
+
+
+
+
+
+ 服务器配置信息
+
+
+
+
+
+ SignalR send data
+
+
+
+
+
+ 建行聚合支付类
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+ 被扫支付
+
+
+
+
+
+
+ 被扫支付
+
+
+
+
+
+
+ 支付结果查询-轮询
+
+
+
+
+
+
+ 支付结果查询-轮询
+
+
+
+
+
+
+ 退款
+
+
+
+
+
+
+ 退款
+
+
+
+
+
+
+ 菜单管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 获取菜单
+
+
+
+
+
+
+
+
+ 查询树形 Table
+
+ 父节点
+ 关键字
+
+
+
+
+ 添加一个菜单
+
+
+
+
+
+
+ 保存菜单权限分配
+
+
+
+
+
+
+ 获取菜单树
+
+
+
+
+
+
+
+ 获取路由树
+
+
+
+
+
+
+ 获取路由树
+
+
+
+
+
+
+ 通过角色获取菜单
+
+
+
+
+
+
+ 更新菜单
+
+
+
+
+
+
+ 删除菜单
+
+
+
+
+
+
+ 导入多条菜单信息
+
+
+
+
+
+
+ 系统接口菜单同步接口
+
+
+
+
+
+ 角色管理
+
+
+
+
+ 获取全部角色
+
+
+
+
+
+
+
+ 添加角色
+
+
+
+
+
+
+ 更新角色
+
+
+
+
+
+
+ 删除角色
+
+
+
+
+
+
+ 分页获取
+
+
+
+
+
+
+
+ 添加计划任务
+
+
+
+
+
+
+ 修改计划任务
+
+
+
+
+
+
+ 删除一个任务
+
+
+
+
+
+
+ 启动计划任务
+
+
+
+
+
+
+ 停止一个计划任务
+
+
+
+
+
+
+ 暂停一个计划任务
+
+
+
+
+
+
+ 恢复一个计划任务
+
+
+
+
+
+
+ 重启一个计划任务
+
+
+
+
+
+
+ 获取任务命名空间
+
+
+
+
+
+ 立即执行任务
+
+
+
+
+
+
+ 获取任务运行日志
+
+
+
+
+
+ 任务概况
+
+
+
+
+
+ 类别管理【无权限】
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取Tibug所有分类
+
+
+
+
+
+ Tibug 管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+ 获取Bug数据列表(带分页)
+ 【无权限】
+
+ 页数
+ 专题类型
+ 关键字
+
+
+
+
+
+ 获取详情【无权限】
+
+
+
+
+
+
+ 添加一个 BUG 【无权限】
+
+
+
+
+
+
+ 更新 bug
+
+
+
+
+
+
+ 删除 bug
+
+
+
+
+
+
+ 测试事务在AOP中的使用
+
+
+
+
+
+
+ 用户管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+
+
+
+
+
+
+ 获取全部用户
+
+
+
+
+
+
+
+ 获取用户详情根据token
+ 【无权限】
+
+ 令牌
+
+
+
+
+ 添加一个用户
+
+
+
+
+
+
+ 更新用户与角色
+
+
+
+
+
+
+ 删除用户
+
+
+
+
+
+
+ 用户角色关系
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+
+
+ 新建用户
+
+
+
+
+
+
+
+ 新建Role
+
+
+
+
+
+
+ 新建用户角色关系
+
+
+
+
+
+
+
+ Values控制器
+
+
+
+
+ 测试Rabbit消息队列发送
+
+
+
+
+ 测试Rabbit消息队列订阅
+
+
+
+
+ 测试SqlSugar二级缓存
+ 可设置过期时间
+ 或通过接口方式更新该数据,也会离开清除缓存
+
+
+
+
+
+ Get方法
+
+
+
+
+
+ 测试Redis消息队列
+
+
+
+
+
+
+ 测试RabbitMQ事件总线
+
+
+
+
+
+
+
+ Get(int id)方法
+
+
+
+
+
+
+ 测试参数是必填项
+
+
+
+
+
+
+ 通过 HttpContext 获取用户信息
+
+ 声明类型,默认 jti
+
+
+
+
+ to redirect by route template name.
+
+
+
+
+ route with template name.
+
+
+
+
+
+ 测试 post 一个对象 + 独立参数
+
+ model实体类参数
+ 独立参数
+
+
+
+ 测试 post 参数
+
+
+
+
+
+
+ 测试多库连接
+
+
+
+
+
+ 测试Fulent做参数校验
+
+
+
+
+
+
+ Put方法
+
+
+
+
+
+
+ Delete方法
+
+
+
+
+
+ 测试接入Apollo获取配置信息
+
+
+
+
+ 通过此处的key格式为 xx:xx:x
+
+
+
+
+ 获取雪花Id
+
+
+
+
+
+ 测试缓存
+
+
+
+
+
+ 雪花Id To DateTime
+
+
+
+
+
+
+ WeChatCompanyController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ WeChatConfigController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ 微信公众号管理
+
+
+
+
+ 构造函数
+
+
+
+
+
+
+ 更新Token
+
+
+
+
+
+
+ 刷新Token
+
+
+
+
+
+
+ 获取模板
+
+
+
+
+
+
+ 获取菜单
+
+
+
+
+
+
+ 更新菜单
+
+
+
+
+
+
+ 获取订阅用户(所有)
+
+
+
+
+
+
+ 入口
+
+
+
+
+
+
+ 获取订阅用户
+
+
+
+
+
+
+
+ 获取一个绑定员工公众号二维码
+
+ 消息
+
+
+
+
+ 推送卡片消息接口
+
+ 卡片消息对象
+
+
+
+
+ 推送卡片消息接口
+
+ 卡片消息对象
+
+
+
+
+ 推送文本消息
+
+ 消息对象
+
+
+
+
+ 通过绑定用户获取微信用户信息(一般用于初次绑定检测)
+
+ 信息
+
+
+
+
+ 用户解绑
+
+ 消息
+
+
+
+
+ WeChatPushLogController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ WeChatSubController
+
+
+
+
+ 构造函数
+
+
+
+
+
+ 获取
+
+ 分页条件
+
+
+
+
+ 获取(id)
+
+ 主键ID
+
+
+
+
+ 添加
+
+
+
+
+
+ 更新
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 批量删除
+
+
+
+
+
+ 查询树形 Table
+
+ 父节点
+ 关键字
+
+
+
+
+ 获取部门树
+
+
+
+
+
+
+ 服务管理
+
+
+
+
+ INacosNamingService
+
+
+
+
+
+
+
+
+
+
+ 系统实例是否启动完成
+
+
+
+
+
+ 获取Nacos 状态
+
+
+
+
+
+ 服务上线
+
+
+
+
+
+ 服务下线
+
+
+
+
+
+ SignalR测试
+
+
+
+
+ 向指定用户发送消息
+
+
+
+
+
+
+
+ 向指定角色发送消息
+
+
+
+
+
+
+
+ 分表demo
+
+
+
+
+ 分页获取数据
+
+
+
+
+
+
+
+
+
+
+ 根据ID获取信息
+
+
+
+
+
+
+ 添加一条测试数据
+
+
+
+
+
+
+ 修改一条测试数据
+
+
+
+
+
+
+ 根据id删除数据
+
+
+
+
+
+
+ SqlSugar 相关测试
+
+
+
+
+ SqlSugar 相关测试
+
+
+
+
+ 测试建表后,SqlSugar缓存
+
+
+
+
+
+ 缓存管理
+
+
+
+
+ 缓存管理
+
+
+
+
+ 获取全部缓存
+
+
+
+
+
+ 获取缓存
+
+
+
+
+
+ 新增
+
+
+
+
+
+ 删除全部缓存
+
+
+
+
+
+ 删除缓存
+
+
+
+
+
+ 数据库管理
+
+
+
+
+ 获取库配置
+
+
+
+
+
+ 获取表信息
+
+ 配置Id
+ 读取类型
+
+
+
+
+ 获取表字段
+
+ 表名
+ ConfigId
+ 读取类型
+
+
+
+
+ 编辑表备注
+
+
+
+
+
+ 编辑列备注
+
+
+
+
+
+ 动态建表 CURD
+
+
+
+
+ 动态type
+
+
+
+
+
+ 动态type 继承BaseEntity
+
+
+
+
+
+ 测试建表
+
+
+
+
+
+ 测试查询
+
+
+
+
+
+ 测试写入
+
+
+
+
+
+ 多租户-多库方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增数据
+
+
+
+
+
+ 多租户-Id方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增业务数据
+
+
+
+
+
+ 多租户-多表方案 测试
+
+
+
+
+ 获取租户下全部业务数据
+
+
+
+
+
+ 新增数据
+
+
+
+
+
+ 租户管理
+
+
+
+
+ 获取全部租户
+
+
+
+
+
+ 获取租户信息
+
+
+
+
+
+ 新增租户信息
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 修改租户信息
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 删除租户
+ 此处只做演示,具体要以实际业务为准
+
+
+
+
+
+ 枚举测试
+
+
+
+
+ 获取学生信息
+
+ 学生类型
+
+
+ 学生信息
+
+
+
+ 学生类型
+
+
+
+
+ 小学生
+
+
+
+
+ 中学生
+
+
+
+
+ 大学生
+
+
+
+
+ 学生姓名
+
+
+
+
+ 学生年龄
+
+
+
+
+ 学生类型
+
+
+
+
+ Summary:全局路由权限公约
+ Remarks:目的是针对不同的路由,采用不同的授权过滤器
+ 如果 controller 上不加 [Authorize] 特性,默认都是 Permission 策略
+ 否则,如果想特例其他授权机制的话,需要在 controller 上带上 [Authorize],然后再action上自定义授权即可,比如 [Authorize(Roles = "Admin")]
+
+
+
+
+ 全局权限过滤器【无效】
+
+
+
+
+ 全局异常错误日志
+
+
+
+
+ 自定义返回格式
+
+
+
+
+
+
+
+ 生产环境的消息
+
+
+
+
+ 开发环境的消息
+
+
+
+
+ 全局路由前缀公约
+
+
+
+
+ 自定义路由 /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/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs
new file mode 100644
index 00000000..d0e9a235
--- /dev/null
+++ b/Blog.Core.Api/Controllers/BlogController.cs
@@ -0,0 +1,278 @@
+using System.Linq.Expressions;
+using System.Text.RegularExpressions;
+using Blog.Core.Common.Helper;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Blog.Core.Model.ViewModels;
+using Blog.Core.SwaggerHelper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Serilog;
+using StackExchange.Profiling;
+using static Blog.Core.Extensions.CustomApiVersion;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 博客管理
+ ///
+ [Produces("application/json")]
+ [Route("api/Blog")]
+ public class BlogController : BaseApiController
+ {
+ public IBlogArticleServices _blogArticleServices { get; set; }
+ private readonly ILogger _logger;
+
+ ///
+ /// 构造函数
+ ///
+ ///
+ ///
+ public BlogController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+
+ ///
+ /// 获取博客列表【无权限】
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ public async Task>> Get(int id, int page = 1, string bcategory = "技术博文", string key = "")
+ {
+ int intPageSize = 6;
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ Expression> whereExpression = a => (a.bcategory == bcategory && a.IsDeleted == false) && ((a.btitle != null && a.btitle.Contains(key)) || (a.bcontent != null && a.bcontent.Contains(key)));
+
+ var pageModelBlog = await _blogArticleServices.QueryPage(whereExpression, page, intPageSize, " bID desc ");
+
+ using (MiniProfiler.Current.Step("获取成功后,开始处理最终数据"))
+ {
+ foreach (var item in pageModelBlog.data)
+ {
+ if (!string.IsNullOrEmpty(item.bcontent))
+ {
+ item.bRemark = (HtmlHelper.ReplaceHtmlTag(item.bcontent)).Length >= 200 ? (HtmlHelper.ReplaceHtmlTag(item.bcontent)).Substring(0, 200) : (HtmlHelper.ReplaceHtmlTag(item.bcontent));
+ int totalLength = 500;
+ if (item.bcontent.Length > totalLength)
+ {
+ item.bcontent = item.bcontent.Substring(0, totalLength);
+ }
+ }
+ }
+ }
+
+ return SuccessPage(pageModelBlog);
+ }
+
+
+ ///
+ /// 获取博客详情
+ ///
+ ///
+ ///
+ [HttpGet("{id}")]
+ //[Authorize(Policy = "Scope_BlogModule_Policy")]
+ [Authorize]
+ public async Task> Get(long id)
+ {
+ return Success(await _blogArticleServices.GetBlogDetails(id));
+ }
+
+
+ ///
+ /// 获取详情【无权限】
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("DetailNuxtNoPer")]
+ 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(long id = 0)
+ {
+ var response = await _blogArticleServices.QueryById(id);
+ if (response != null && response.bsubmitter.IsNotEmptyOrNull())
+ {
+ string Url = @"^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?$";
+ if (Regex.IsMatch(response.bsubmitter, Url))
+ {
+ response.btraffic += 1;
+ await _blogArticleServices.Update(response);
+ return Redirect(response.bsubmitter);
+ }
+
+ }
+
+ return Ok();
+ }
+
+ [HttpGet]
+ [Route("GetBlogsByTypesForMVP")]
+ public async Task>> GetBlogsByTypesForMVP(string types = "", int id = 0)
+ {
+ if (types.IsNotEmptyOrNull())
+ {
+ 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() { });
+ }
+
+ [HttpGet]
+ [Route("GetBlogByIdForMVP")]
+ public async Task> GetBlogByIdForMVP(long id = 0)
+ {
+ if (id > 0)
+ {
+ return Success(await _blogArticleServices.QueryById(id));
+ }
+ return Success(new BlogArticle());
+ }
+
+ ///
+ /// 获取博客测试信息 v2版本
+ ///
+ ///
+ [HttpGet]
+ ////MVC自带特性 对 api 进行组管理
+ //[ApiExplorerSettings(GroupName = "v2")]
+ ////路径 如果以 / 开头,表示绝对路径,反之相对 controller 的想u地路径
+ //[Route("/api/v2/blog/Blogtest")]
+ //和上边的版本控制以及路由地址都是一样的
+
+ [CustomRoute(ApiVersions.V2, "Blogtest")]
+ public MessageModel V2_Blogtest()
+ {
+ return Success("我是第二版的博客信息");
+ }
+
+ ///
+ /// 添加博客【无权限】
+ ///
+ ///
+ ///
+ [HttpPost]
+ //[Authorize(Policy = "Scope_BlogModule_Policy")]
+ [Authorize]
+ public async Task> Post([FromBody] BlogArticle blogArticle)
+ {
+ if (blogArticle.btitle.Length > 5 && blogArticle.bcontent.Length > 50)
+ {
+
+ blogArticle.bCreateTime = DateTime.Now;
+ blogArticle.bUpdateTime = DateTime.Now;
+ blogArticle.IsDeleted = false;
+ blogArticle.bcategory = "技术博文";
+ var id = (await _blogArticleServices.Add(blogArticle));
+ return id > 0 ? Success(id.ObjToString()) : Failed("添加失败");
+ }
+ else
+ {
+ return Failed("文章标题不能少于5个字符,内容不能少于50个字符!");
+ }
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("AddForMVP")]
+ [Authorize(Permissions.Name)]
+ public async Task> AddForMVP([FromBody] BlogArticle blogArticle)
+ {
+ blogArticle.bCreateTime = DateTime.Now;
+ blogArticle.bUpdateTime = DateTime.Now;
+ blogArticle.IsDeleted = false;
+ var id = (await _blogArticleServices.Add(blogArticle));
+ return id > 0 ? Success(id.ObjToString()) : Failed("添加失败");
+ }
+ ///
+ /// 更新博客信息
+ ///
+ ///
+ ///
+ // PUT: api/User/5
+ [HttpPut]
+ [Route("Update")]
+ [Authorize(Permissions.Name)]
+ public async Task> Put([FromBody] BlogArticle BlogArticle)
+ {
+ if (BlogArticle != null && BlogArticle.bID > 0)
+ {
+ var model = await _blogArticleServices.QueryById(BlogArticle.bID);
+
+ if (model != null)
+ {
+ model.btitle = BlogArticle.btitle;
+ model.bcategory = BlogArticle.bcategory;
+ model.bsubmitter = BlogArticle.bsubmitter;
+ model.bcontent = BlogArticle.bcontent;
+ model.btraffic = BlogArticle.btraffic;
+
+ if (await _blogArticleServices.Update(model))
+ {
+ return Success(BlogArticle?.bID.ObjToString());
+ }
+ }
+ }
+ return Failed("更新失败");
+ }
+
+
+
+ ///
+ /// 删除博客
+ ///
+ ///
+ ///
+ [HttpDelete]
+ [Authorize(Permissions.Name)]
+ [Route("Delete")]
+ 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("删除失败");
+ }
+ return Failed("入参无效");
+ }
+ ///
+ /// apache jemeter 压力测试
+ /// 更新接口
+ ///
+ ///
+ [HttpGet]
+ [Route("ApacheTestUpdate")]
+ public async Task> ApacheTestUpdate()
+ {
+ return Success(await _blogArticleServices.Update(new { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", bID = 1 }), "更新成功");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs
new file mode 100644
index 00000000..553dee75
--- /dev/null
+++ b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs
@@ -0,0 +1,179 @@
+using Blog.Core.Common;
+using Blog.Core.Common.DB;
+using Blog.Core.Common.Seed;
+using Blog.Core.Model;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Hosting;
+using SqlSugar;
+using System.Linq;
+
+namespace Blog.Core.Controllers
+{
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ //[Authorize(Permissions.Name)]
+ public class DbFirstController : ControllerBase
+ {
+ private readonly SqlSugarScope _sqlSugarClient;
+ private readonly IWebHostEnvironment Env;
+
+ ///
+ /// 构造函数
+ ///
+ public DbFirstController(ISqlSugarClient sqlSugarClient, IWebHostEnvironment env)
+ {
+ _sqlSugarClient = sqlSugarClient as SqlSugarScope;
+ Env = env;
+ }
+
+ ///
+ /// 获取 整体框架 文件(主库)(一般可用第一次生成)
+ ///
+ ///
+ [HttpGet]
+ public MessageModel GetFrameFiles()
+ {
+ var data = new MessageModel() { success = true, msg = "" };
+ data.response += @"file path is:C:\my-file\}";
+ var isMuti = BaseDBConfig.IsMulti;
+ if (Env.IsDevelopment())
+ {
+ data.response += $"Controller层生成:{FrameSeed.CreateControllers(_sqlSugarClient)} || ";
+
+ BaseDBConfig.ValidConfig.ForEach(m =>
+ {
+ _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)} || ";
+ });
+
+ // 切回主库
+ _sqlSugarClient.ChangeDatabase(MainDb.CurrentDbConnId.ToLower());
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+
+ ///
+ /// 获取仓储层和服务层(需指定表名和数据库)
+ ///
+ /// 数据库链接名称
+ /// 需要生成的表名
+ ///
+ [HttpPost]
+ public MessageModel GetFrameFilesByTableNames([FromBody]string[] tableNames, [FromQuery]string ConnID = null)
+ {
+ ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
+
+ var isMuti = BaseDBConfig.IsMulti;
+ var data = new MessageModel() { success = true, msg = "" };
+ if (Env.IsDevelopment())
+ {
+ data.response += $"库{ConnID}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+ ///
+ /// 获取实体(需指定表名和数据库)
+ ///
+ /// 数据库链接名称
+ /// 需要生成的表名
+ ///
+ [HttpPost]
+ public MessageModel GetFrameFilesByTableNamesForEntity([FromBody] string[] tableNames, [FromQuery] string ConnID = null)
+ {
+ ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
+
+ var isMuti = BaseDBConfig.IsMulti;
+ var data = new MessageModel() { success = true, msg = "" };
+ if (Env.IsDevelopment())
+ {
+ data.response += $"库{ConnID}-Models层生成:{FrameSeed.CreateModels(_sqlSugarClient, ConnID, isMuti, tableNames)}";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+ return data;
+ }
+ ///
+ /// 获取控制器(需指定表名和数据库)
+ ///
+ /// 数据库链接名称
+ /// 需要生成的表名
+ ///
+ [HttpPost]
+ public MessageModel GetFrameFilesByTableNamesForController([FromBody] string[] tableNames, [FromQuery] string ConnID = null)
+ {
+ ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
+
+ var isMuti = BaseDBConfig.IsMulti;
+ var data = new MessageModel() { success = true, msg = "" };
+ if (Env.IsDevelopment())
+ {
+ data.response += $"库{ConnID}-Controllers层生成:{FrameSeed.CreateControllers(_sqlSugarClient, ConnID, isMuti, tableNames)}";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+ return data;
+ }
+
+ ///
+ /// DbFrist 根据数据库表名 生成整体框架,包含Model层(一般可用第一次生成)
+ ///
+ /// 数据库链接名称
+ /// 需要生成的表名
+ ///
+ [HttpPost]
+ public MessageModel GetAllFrameFilesByTableNames([FromBody]string[] tableNames, [FromQuery]string ConnID = null)
+ {
+ ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID;
+
+ var isMuti = BaseDBConfig.IsMulti;
+ var data = new MessageModel() { success = true, msg = "" };
+ if (Env.IsDevelopment())
+ {
+ _sqlSugarClient.ChangeDatabase(ConnID.ToLower());
+ data.response += $"Controller层生成:{FrameSeed.CreateControllers(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-Model层生成:{FrameSeed.CreateModels(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ data.response += $"库{ConnID}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, ConnID, isMuti, tableNames)} || ";
+ // 切回主库
+ _sqlSugarClient.ChangeDatabase(MainDb.CurrentDbConnId.ToLower());
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs
new file mode 100644
index 00000000..7865cc69
--- /dev/null
+++ b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs
@@ -0,0 +1,359 @@
+using Blog.Core.Common.Helper;
+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
+{
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ //[Authorize(Permissions.Name)]
+ public class MigrateController : ControllerBase
+ {
+ 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(IUnitOfWorkManage unitOfWorkManage,
+ IRoleModulePermissionServices roleModulePermissionServices,
+ IUserRoleServices userRoleServices,
+ IRoleServices roleServices,
+ IPermissionServices permissionServices,
+ IModuleServices moduleServices,
+ IDepartmentServices departmentServices,
+ ISysUserInfoServices sysUserInfoServices,
+ IWebHostEnvironment env)
+ {
+ _unitOfWorkManage = unitOfWorkManage;
+ _roleModulePermissionServices = roleModulePermissionServices;
+ _userRoleServices = userRoleServices;
+ _roleServices = roleServices;
+ _permissionServices = permissionServices;
+ _moduleServices = moduleServices;
+ _departmentServices = departmentServices;
+ _sysUserInfoServices = sysUserInfoServices;
+ _env = env;
+ }
+
+
+ ///
+ /// 获取权限部分Map数据(从库)
+ /// 迁移到新库(主库)
+ ///
+ ///
+ [HttpGet]
+ 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.PermissionId >= filterPermissionId).ToList();
+
+ InitPermissionTree(permissions, permissionsAllList, apiList);
+
+ 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)
+ {
+ // 角色信息,防止重复添加,做了判断
+ if (item.Role != null)
+ {
+ var isExit = (await _roleServices.Query(d => d.Name == item.Role.Name && d.IsDeleted == false)).FirstOrDefault();
+ if (isExit == null)
+ {
+ rid = await _roleServices.Add(item.Role);
+ Console.WriteLine($"Role Added:{item.Role.Name}");
+ }
+ else
+ {
+ rid = isExit.Id;
+ }
+ }
+
+ 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)
+ {
+ rpmid = await _roleModulePermissionServices.Add(new RoleModulePermission()
+ {
+ IsDeleted = false,
+ CreateTime = DateTime.Now,
+ ModifyTime = DateTime.Now,
+ ModuleId = mid,
+ PermissionId = pid,
+ RoleId = rid,
+ });
+ Console.WriteLine($"RMP Added:{rpmid}");
+ }
+
+ }
+
+
+ _unitOfWorkManage.CommitTran();
+
+ data.success = true;
+ data.msg = "导入成功!";
+ }
+ catch (Exception)
+ {
+ _unitOfWorkManage.RollbackTran();
+
+ }
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ return data;
+ }
+
+
+ ///
+ /// 权限数据库导出tsv
+ ///
+ ///
+ [HttpGet]
+ 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.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.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.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.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;
+ data.msg = "生成成功!";
+ }
+ else
+ {
+ data.success = false;
+ data.msg = "当前不处于开发模式,代码生成不可用!";
+ }
+
+ 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/HealthCheckController.cs b/Blog.Core.Api/Controllers/HealthCheckController.cs
new file mode 100644
index 00000000..04fac9d6
--- /dev/null
+++ b/Blog.Core.Api/Controllers/HealthCheckController.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 健康检查
+ ///
+ [Route("[controller]")]
+ [ApiController]
+ public class HealthCheckController : ControllerBase
+ {
+ ///
+ /// 健康检查接口
+ ///
+ ///
+ [HttpGet]
+ public IActionResult Get()
+ {
+ return Ok();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/ImgController.cs b/Blog.Core.Api/Controllers/ImgController.cs
new file mode 100644
index 00000000..5ba85388
--- /dev/null
+++ b/Blog.Core.Api/Controllers/ImgController.cs
@@ -0,0 +1,145 @@
+using Blog.Core.Model;
+using Blog.Core.Model.ViewModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 图片管理
+ ///
+ [Route("api/[controller]")]
+ [ApiController]
+ [Authorize]
+ 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()
+ {
+ string foldername = "";
+ string filepath = Path.Combine(_env.WebRootPath, foldername, "测试下载中文名称的图片.png");
+ var stream = System.IO.File.OpenRead(filepath);
+ string fileExt = ".jpg"; // 这里可以写一个获取文件扩展名的方法,获取扩展名
+ //获取文件的ContentType
+ var provider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider();
+ var memi = provider.Mappings[fileExt];
+ var fileName = Path.GetFileName(filepath);
+
+
+ return File(stream, memi, fileName);
+ }
+
+ ///
+ /// 上传图片,多文件
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("/images/Upload/Pic")]
+ public async Task> InsertPicture([FromForm]UploadFileDto dto)
+ {
+
+ 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 foldername = "images";
+ string folderpath = Path.Combine(_env.WebRootPath, foldername);
+ if (!Directory.Exists(folderpath))
+ {
+ Directory.CreateDirectory(folderpath);
+ }
+ foreach (var file in allowedFile)
+ {
+ 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))
+ {
+ await file.CopyToAsync(stream);
+ }
+ }
+
+ var excludeFiles = dto.file.Except(allowedFile);
+
+ if (excludeFiles.Any())
+ {
+ var infoMsg = $"{string.Join('、', excludeFiles.Select(c => c.FileName))} 图片格式错误";
+ return Success(null, infoMsg);
+ }
+
+ return Success(null, "上传成功");
+
+ }
+
+
+
+ [HttpGet]
+ [Route("/images/Down/Bmd")]
+ [AllowAnonymous]
+ public FileStreamResult DownBmd(string filename)
+ {
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+ // 前端 blob 接收,具体查看前端admin代码
+ string filepath = Path.Combine(_env.WebRootPath, Path.GetFileName(filename));
+ if (System.IO.File.Exists(filepath))
+ {
+ var stream = System.IO.File.OpenRead(filepath);
+ //string fileExt = ".bmd";
+ //获取文件的ContentType
+ var provider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider();
+ //var memi = provider.Mappings[fileExt];
+ var fileName = Path.GetFileName(filepath);
+
+ HttpContext.Response.Headers.Add("fileName", fileName);
+
+ return File(stream, "application/octet-stream", fileName);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ // POST: api/Img
+ [HttpPost]
+ public void Post([FromBody] object formdata)
+ {
+ }
+
+ // PUT: api/Img/5
+ [HttpPut("{id}")]
+ public void Put(int id, [FromBody] string value)
+ {
+ }
+
+ // DELETE: api/ApiWithActions/5
+ [HttpDelete("{id}")]
+ public void Delete(int id)
+ {
+ }
+ }
+
+}
diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs
new file mode 100644
index 00000000..5b40773d
--- /dev/null
+++ b/Blog.Core.Api/Controllers/LoginController.cs
@@ -0,0 +1,346 @@
+using Blog.Core.AuthHelper;
+using Blog.Core.AuthHelper.OverWrite;
+using Blog.Core.Common.Helper;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.ViewModels;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Blog.Core.Common.Swagger;
+
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 登录管理【无权限】
+ ///
+ [Produces("application/json")]
+ [Route("api/Login")]
+ [AllowAnonymous]
+ public class LoginController : BaseApiController
+ {
+ readonly ISysUserInfoServices _sysUserInfoServices;
+ readonly IUserRoleServices _userRoleServices;
+ readonly IRoleServices _roleServices;
+ readonly PermissionRequirement _requirement;
+ private readonly IRoleModulePermissionServices _roleModulePermissionServices;
+ private readonly ILogger _logger;
+
+ ///
+ /// 构造函数注入
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [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};
+
+ jwtStr = JwtHelper.IssueJwt(tokenModel);
+ suc = true;
+ }
+ else
+ {
+ jwtStr = "login fail!!!";
+ }
+
+ return new MessageModel()
+ {
+ success = suc,
+ msg = suc ? "获取成功" : "获取失败",
+ response = jwtStr
+ };
+ }
+
+
+ ///
+ /// 获取JWT的方法2:给Nuxt提供
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("GetTokenNuxt")]
+ public MessageModel GetJwtStrForNuxt(string name, string pass)
+ {
+ string jwtStr = string.Empty;
+ bool suc = false;
+ //这里就是用户登录以后,通过数据库去调取数据,分配权限的操作
+ //这里直接写死了
+ if (name == "admins" && pass == "admins")
+ {
+ TokenModelJwt tokenModel = new TokenModelJwt
+ {
+ Uid = 1,
+ Role = "Admin"
+ };
+
+ jwtStr = JwtHelper.IssueJwt(tokenModel);
+ suc = true;
+ }
+ else
+ {
+ jwtStr = "login fail!!!";
+ }
+
+ var result = new
+ {
+ data = new {success = suc, token = jwtStr}
+ };
+
+ return new MessageModel()
+ {
+ success = suc,
+ msg = suc ? "获取成功" : "获取失败",
+ response = jwtStr
+ };
+ }
+
+ #endregion
+
+
+ ///
+ /// 获取JWT的方法3:整个系统主要方法
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("JWTToken3.0")]
+ public async Task> GetJwtToken3(string name = "", string pass = "")
+
+ {
+ string jwtStr = string.Empty;
+
+ if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass))
+ return Failed("用户名或密码不能为空");
+
+ pass = MD5Helper.MD5Encrypt32(pass);
+
+ 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
+ {
+ new Claim(ClaimTypes.Name, name),
+ 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)));
+
+
+ // ids4和jwt切换
+ // jwt
+ if (!Permissions.IsUseIds4)
+ {
+ var data = await _roleModulePermissionServices.RoleModuleMaps();
+ var list = (from item in data
+ where item.IsDeleted == false
+ orderby item.Id
+ select new PermissionItem
+ {
+ Url = item.Module?.LinkUrl,
+ Role = item.Role?.Name.ObjToString(),
+ }).ToList();
+
+ _requirement.Permissions = list;
+ }
+
+ var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
+ return Success(token, "获取成功");
+ }
+ else
+ {
+ return Failed("认证失败");
+ }
+ }
+
+ [HttpGet]
+ [Route("GetJwtTokenSecret")]
+ public async Task> GetJwtTokenSecret(string name = "", string pass = "")
+ {
+ var rlt = await GetJwtToken3(name, pass);
+ return rlt;
+ }
+
+ ///
+ /// 请求刷新Token(以旧换新)
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("RefreshToken")]
+ public async Task> RefreshToken(string token = "")
+ {
+ string jwtStr = string.Empty;
+
+ if (string.IsNullOrEmpty(token))
+ return Failed("token无效,请重新登录!");
+ var tokenModel = JwtHelper.SerializeJwt(token);
+ if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0)
+ {
+ var user = await _sysUserInfoServices.QueryById(tokenModel.Uid);
+ var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
+ if (value != null && user.CriticalModifyTime > value.ObjToDate())
+ {
+ 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.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)));
+
+ //用户标识
+ var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
+ identity.AddClaims(claims);
+
+ var refreshToken = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
+ return Success(refreshToken, "获取成功");
+ }
+ }
+
+ return Failed("认证失败!");
+ }
+
+ ///
+ /// 获取JWT的方法4:给 JSONP 测试
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("jsonp")]
+ public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30,
+ int expiresAbsoulute = 30)
+ {
+ TokenModelJwt tokenModel = new TokenModelJwt
+ {
+ Uid = id,
+ Role = sub
+ };
+
+ string jwtStr = JwtHelper.IssueJwt(tokenModel);
+
+ string response = string.Format("\"value\":\"{0}\"", jwtStr);
+ string call = callBack + "({" + response + "})";
+ Response.WriteAsync(call);
+ }
+
+
+ ///
+ /// 测试 MD5 加密字符串
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("Md5Password")]
+ 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
new file mode 100644
index 00000000..3b286808
--- /dev/null
+++ b/Blog.Core.Api/Controllers/ModuleController.cs
@@ -0,0 +1,173 @@
+using System.Linq.Expressions;
+using Blog.Core.Common.HttpContextUser;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 接口管理
+ ///
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ [Authorize(Permissions.Name)]
+ public class ModuleController : BaseApiController
+ {
+ readonly IModuleServices _moduleServices;
+ readonly IUser _user;
+
+
+ public ModuleController(IModuleServices moduleServices, IUser user)
+ {
+ _moduleServices = moduleServices;
+ _user = user;
+ }
+
+ ///
+ /// 获取全部接口api
+ ///
+ ///
+ ///
+ ///
+ // GET: api/User
+ [HttpGet]
+ public async Task>> Get(int page = 1, string key = "", int pageSize = 50)
+ {
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ Expression> whereExpression = a => a.IsDeleted != true && ((a.Name != null && a.Name.Contains(key) || (a.LinkUrl != null && a.LinkUrl.Contains(key))));
+
+ PageModel data = new PageModel();
+
+ if (page == -1)
+ {
+ var modules = await _moduleServices.Query(whereExpression, " Id desc ");
+ data.data = modules;
+ }
+ else
+ {
+ data = await _moduleServices.QueryPage(whereExpression, page, pageSize, " Id desc ");
+ }
+
+
+ return Success(data, "获取成功");
+
+
+ }
+
+ // GET: api/User/5
+ [HttpGet("{id}")]
+ public string Get(string id)
+ {
+ return "value";
+ }
+
+ ///
+ /// 添加一条接口信息
+ ///
+ ///
+ ///
+ // POST: api/User
+ [HttpPost]
+ public async Task> Post([FromBody] Modules module)
+ {
+ module.CreateId = _user.ID;
+ module.CreateBy = _user.Name;
+ var id = (await _moduleServices.Add(module));
+ return id > 0 ? Success(id.ObjToString(), "添加成功") : Failed();
+
+ }
+
+ ///
+ /// 更新接口信息
+ ///
+ ///
+ ///
+ // PUT: api/User/5
+ [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;
+ if (module == null || module.Id <= 0)
+ return Failed("缺少参数");
+ return await _moduleServices.Update(module) ? Success(module?.Id.ObjToString(), "更新成功") : Failed();
+ }
+
+ ///
+ /// 删除一条接口
+ ///
+ ///
+ ///
+ // DELETE: api/ApiWithActions/5
+ [HttpDelete]
+ public async Task> Delete(long id)
+ {
+ 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 module = modules[i];
+ if (module != null)
+ {
+ module.CreateId = _user.ID;
+ module.CreateBy = _user.Name;
+ ids += (await _moduleServices.Add(module));
+ sucCount++;
+ }
+ }
+ return ids.IsNotEmptyOrNull() ? Success(ids, $"{sucCount}条数据添加成功") : Failed();
+ }
+ }
+}
diff --git a/Blog.Core.Api/Controllers/MonitorController.cs b/Blog.Core.Api/Controllers/MonitorController.cs
new file mode 100644
index 00000000..d707d497
--- /dev/null
+++ b/Blog.Core.Api/Controllers/MonitorController.cs
@@ -0,0 +1,280 @@
+using Blog.Core.Common;
+using Blog.Core.Common.Helper;
+using Blog.Core.Common.LogHelper;
+using Blog.Core.Hubs;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.ViewModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.SignalR;
+using Newtonsoft.Json;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Blog.Core.Extensions.Middlewares;
+
+namespace Blog.Core.Controllers
+{
+ [Route("api/[Controller]/[action]")]
+ [ApiController]
+ [AllowAnonymous]
+ 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)
+ {
+ _hubContext = hubContext;
+ _env = env;
+ _applicationUserServices = applicationUserServices;
+ _logger = logger;
+ }
+
+ ///
+ /// 服务器配置信息
+ ///
+ ///
+ [HttpGet]
+ public MessageModel Server()
+ {
+ return Success(new ServerViewModel()
+ {
+ 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)
+ }, "获取服务器配置信息成功");
+ }
+
+
+ ///
+ /// SignalR send data
+ ///
+ ///
+ // GET: api/Logs
+ [HttpGet]
+ public MessageModel> Get()
+ {
+ if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool())
+ {
+ _hubContext.Clients.All.SendAsync("ReceiveUpdate", "执行成功").Wait();
+ }
+
+ return Success>(null, "执行成功");
+ }
+
+
+ [HttpGet]
+ public MessageModel GetRequestApiinfoByWeek()
+ {
+ //后续补充扩展Log
+ return Success(new RequestApiWeekView(), "成功");
+ }
+
+ [HttpGet]
+ public MessageModel GetAccessApiByDate()
+ {
+ //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()
+ //};
+
+ //后续补充扩展Log
+ return Success(new AccessApiDateView(), "获取成功");
+ }
+
+ private List GetAccessLogsToday(IWebHostEnvironment environment)
+ {
+ List userAccessModels = new();
+ //后续补充扩展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;
+ }
+
+ [HttpGet]
+ public MessageModel GetActiveUsers([FromServices] IWebHostEnvironment environment)
+ {
+ var accessLogsToday = GetAccessLogsToday(environment).Where(d => d.BeginTime.ObjToDate() >= DateTime.Today);
+
+ var Logs = accessLogsToday.OrderByDescending(d => d.BeginTime).Take(50).ToList();
+
+ 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();
+
+ int activeUsersCount = activeUsers.Count;
+ activeUsers = activeUsers.OrderByDescending(d => d.count).Take(10).ToList();
+
+ //return new MessageModel()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = new WelcomeInitData()
+ // {
+ // activeUsers = activeUsers,
+ // activeUserCount = activeUsersCount,
+ // errorCount = errorCountToday,
+ // logs = Logs,
+ // activeCount = GetAccessLogsTrend(environment)
+ // }
+ //};
+
+ return Success(new WelcomeInitData()
+ {
+ activeUsers = activeUsers,
+ activeUserCount = activeUsersCount,
+ errorCount = default,
+ logs = Logs,
+ activeCount = GetAccessLogsTrend(environment)
+ }, "获取成功");
+ }
+
+ [HttpGet]
+ public async Task> GetIds4Users()
+ {
+ List apiDates = new List();
+
+ if (_applicationUserServices.IsEnable())
+ {
+ var users = await _applicationUserServices.Query(d => d.tdIsDelete == false);
+
+ apiDates = (from n in users
+ group n by new { n.birth.Date }
+ into g
+ select new ApiDate
+ {
+ date = g.Key?.Date.ToString("yyyy-MM-dd"),
+ count = g.Count(),
+ }).ToList();
+
+ apiDates = apiDates.OrderByDescending(d => d.date).Take(30).ToList();
+ }
+
+
+ if (apiDates.Count == 0)
+ {
+ apiDates.Add(new ApiDate()
+ {
+ date = "没数据,或未开启相应接口服务",
+ count = 0
+ });
+ }
+ //return new MessageModel()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = new AccessApiDateView
+ // {
+ // columns = new string[] { "date", "count" },
+ // rows = apiDates.OrderBy(d => d.date).ToList(),
+ // }
+ //};
+
+ return Success(new AccessApiDateView
+ {
+ columns = new string[] { "date", "count" },
+ rows = apiDates.OrderBy(d => d.date).ToList(),
+ }, "获取成功");
+ }
+ }
+
+ public class WelcomeInitData
+ {
+ public List activeUsers { get; set; }
+ public int activeUserCount { get; set; }
+ public List logs { get; set; }
+ public int errorCount { get; set; }
+ public List activeCount { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/NacosController.cs b/Blog.Core.Api/Controllers/NacosController.cs
new file mode 100644
index 00000000..e5223851
--- /dev/null
+++ b/Blog.Core.Api/Controllers/NacosController.cs
@@ -0,0 +1,140 @@
+using Blog.Core.Common.Helper;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Nacos.V2;
+
+namespace Blog.Core.Api.Controllers
+{
+ ///
+ /// 服务管理
+ ///
+ [Produces("application/json")]
+ [Route("api/[Controller]/[action]")]
+ [Authorize(Permissions.Name)]
+ public class NacosController : BaseApiController
+ {
+
+ #region 变量
+
+ ///
+ /// INacosNamingService
+ ///
+ private readonly INacosNamingService NacosNamingService;
+
+ #endregion
+
+ #region 重载
+ ///
+ ///
+ ///
+ ///
+ public NacosController(INacosNamingService nacosNamingService)
+ {
+ NacosNamingService = nacosNamingService;
+ }
+
+ #endregion
+
+
+ ///
+ /// 系统实例是否启动完成
+ ///
+ ///
+ [HttpGet]
+
+ public MessageModel CheckSystemStartFinish()
+ {
+ //********************* 用当前接口做基本健康检查 确定 基础服务 数据库 缓存都已正常启动*****
+ // 然后再进行服务上线
+ var data = new MessageModel();
+ // *************** 此处请求一下db 跟redis连接等 项目中简介 保证项目已全部启动
+ data.success = true;
+ data.msg = "SUCCESS";
+ return data;
+
+ }
+
+
+ ///
+ /// 获取Nacos 状态
+ ///
+ ///
+ [HttpGet]
+ public async Task> GetStatus()
+ {
+ var data = new MessageModel();
+ var instances = await NacosNamingService.GetAllInstances(JsonConfigSettings.NacosServiceName);
+ if (instances == null || instances.Count == 0)
+ {
+ data.status = 406;
+ data.msg = "DOWN";
+ data.success = false;
+ return data;
+ }
+ // 获取当前程序IP
+ var currentIp = IpHelper.GetCurrentIp(null);
+ bool isUp = false;
+ instances.ForEach(item =>
+ {
+ if (item.Ip == currentIp)
+ isUp = true;
+ });
+ // var baseUrl = await NacosNamingService.GetServerStatus();
+ if (isUp)
+ {
+ data.status = 200;
+ data.msg = "UP";
+ data.success = true;
+ return data;
+ }
+ else
+ {
+ data.status = 406;
+ data.msg = "DOWN";
+ data.success = false;
+ return data;
+ }
+ }
+
+ ///
+ /// 服务上线
+ ///
+ ///
+
+ [HttpGet]
+ public async Task> Register()
+ {
+ var data = new MessageModel();
+ var instance = new Nacos.V2.Naming.Dtos.Instance()
+ {
+ ServiceName = JsonConfigSettings.NacosServiceName,
+ ClusterName = Nacos.V2.Common.Constants.DEFAULT_CLUSTER_NAME,
+ Ip = IpHelper.GetCurrentIp(null),
+ Port = JsonConfigSettings.NacosPort,
+ Enabled = true,
+ Weight = 100,
+ Metadata = JsonConfigSettings.NacosMetadata
+ };
+ await NacosNamingService.RegisterInstance(JsonConfigSettings.NacosServiceName, Nacos.V2.Common.Constants.DEFAULT_GROUP, instance);
+ data.success = true;
+ data.msg = "SUCCESS";
+ return data;
+ }
+
+ ///
+ /// 服务下线
+ ///
+ ///
+ [HttpGet]
+ public async Task> Deregister()
+ {
+ var data = new MessageModel();
+ await NacosNamingService.DeregisterInstance(JsonConfigSettings.NacosServiceName, Nacos.V2.Common.Constants.DEFAULT_GROUP, IpHelper.GetCurrentIp(null), JsonConfigSettings.NacosPort);
+ data.success = true;
+ data.msg = "SUCCESS";
+ return data;
+ }
+ }
+}
diff --git a/Blog.Core.Api/Controllers/PayController.cs b/Blog.Core.Api/Controllers/PayController.cs
new file mode 100644
index 00000000..6c05c249
--- /dev/null
+++ b/Blog.Core.Api/Controllers/PayController.cs
@@ -0,0 +1,101 @@
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.ViewModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 建行聚合支付类
+ ///
+ [Produces("application/json")]
+ [Route("api/Pay")]
+ [Authorize(Permissions.Name)]
+ public class PayController : Controller
+ {
+ private readonly ILogger _logger;
+ private readonly IPayServices _payServices;
+ ///
+ /// 构造函数
+ ///
+ ///
+ ///
+ public PayController(ILogger logger, IPayServices payServices)
+ {
+ _logger = logger;
+ _payServices = payServices;
+ }
+ ///
+ /// 被扫支付
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("Pay")]
+ public async Task> PayGet([FromQuery]PayNeedModel payModel)
+ {
+ return await _payServices.Pay(payModel);
+ }
+ ///
+ /// 被扫支付
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("Pay")]
+ public async Task> PayPost([FromBody]PayNeedModel payModel)
+ {
+ return await _payServices.Pay(payModel);
+ }
+ ///
+ /// 支付结果查询-轮询
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("PayCheck")]
+ public async Task> PayCheckGet([FromQuery]PayNeedModel payModel)
+ {
+ return await _payServices.PayCheck(payModel, 1);
+ }
+ ///
+ /// 支付结果查询-轮询
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("PayCheck")]
+ public async Task> PayCheckPost([FromBody]PayNeedModel payModel)
+ {
+ return await _payServices.PayCheck(payModel, 1);
+ }
+ ///
+ /// 退款
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("PayRefund")]
+ public async Task> PayRefundGet([FromQuery]PayRefundNeedModel payModel)
+ {
+ return await _payServices.PayRefund(payModel);
+ }
+ ///
+ /// 退款
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("PayRefund")]
+ public async Task> PayRefundPost([FromBody]PayRefundNeedModel payModel)
+ {
+ return await _payServices.PayRefund(payModel);
+ }
+
+
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs
new file mode 100644
index 00000000..ccdb3dc4
--- /dev/null
+++ b/Blog.Core.Api/Controllers/PermissionController.cs
@@ -0,0 +1,799 @@
+using Blog.Core.AuthHelper;
+using Blog.Core.AuthHelper.OverWrite;
+using Blog.Core.Common;
+using Blog.Core.Common.Helper;
+using Blog.Core.Common.HttpContextUser;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Blog.Core.Repository.UnitOfWorks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Security.Claims;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 菜单管理
+ ///
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ [Authorize(Permissions.Name)]
+ public class PermissionController : BaseApiController
+ {
+ readonly IUnitOfWorkManage _unitOfWorkManage;
+ readonly IPermissionServices _permissionServices;
+ readonly IModuleServices _moduleServices;
+ readonly IRoleModulePermissionServices _roleModulePermissionServices;
+ readonly IUserRoleServices _userRoleServices;
+ private readonly IHttpClientFactory _httpClientFactory;
+ readonly IHttpContextAccessor _httpContext;
+ readonly IUser _user;
+ private readonly PermissionRequirement _requirement;
+
+ ///
+ /// 构造函数
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PermissionController(IPermissionServices permissionServices, IModuleServices moduleServices,
+ IRoleModulePermissionServices roleModulePermissionServices, IUserRoleServices userRoleServices,
+ IUnitOfWorkManage unitOfWorkManage,
+ IHttpClientFactory httpClientFactory,
+ IHttpContextAccessor httpContext, IUser user, PermissionRequirement requirement)
+ {
+ _permissionServices = permissionServices;
+ _unitOfWorkManage = unitOfWorkManage;
+ _moduleServices = moduleServices;
+ _roleModulePermissionServices = roleModulePermissionServices;
+ _userRoleServices = userRoleServices;
+ this._httpClientFactory = httpClientFactory;
+ _httpContext = httpContext;
+ _user = user;
+ _requirement = requirement;
+ }
+
+ ///
+ /// 获取菜单
+ ///
+ ///
+ ///
+ ///
+ ///
+ // GET: api/User
+ [HttpGet]
+ public async Task>> Get(int page = 1, string key = "", int pageSize = 50)
+ {
+ PageModel permissions = new PageModel();
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ permissions = await _permissionServices.QueryPage(a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key)), page, pageSize, " Id desc ");
+
+
+ #region 单独处理
+
+ var apis = await _moduleServices.Query(d => d.IsDeleted == false);
+ var permissionsView = permissions.data;
+
+ var permissionAll = await _permissionServices.Query(d => d.IsDeleted != true);
+ foreach (var item in permissionsView)
+ {
+ List pidarr = new()
+ {
+ item.Pid
+ };
+ if (item.Pid > 0)
+ {
+ pidarr.Add(0);
+ }
+ var parent = permissionAll.FirstOrDefault(d => d.Id == item.Pid);
+
+ while (parent != null)
+ {
+ pidarr.Add(parent.Id);
+ parent = permissionAll.FirstOrDefault(d => d.Id == parent.Pid);
+ }
+
+
+ item.PidArr = pidarr.OrderBy(d => d).Distinct().ToList();
+ foreach (var pid in item.PidArr)
+ {
+ var per = permissionAll.FirstOrDefault(d => d.Id == pid);
+ item.PnameArr.Add((per != null ? per.Name : "根节点") + "/");
+ //var par = Permissions.Where(d => d.Pid == item.Id ).ToList();
+ //item.PCodeArr.Add((per != null ? $"/{per.Code}/{item.Code}" : ""));
+ //if (par.Count == 0 && item.Pid == 0)
+ //{
+ // item.PCodeArr.Add($"/{item.Code}");
+ //}
+ }
+
+ item.MName = apis.FirstOrDefault(d => d.Id == item.Mid)?.LinkUrl;
+ }
+
+ permissions.data = permissionsView;
+
+ #endregion
+
+
+ //return new MessageModel>()
+ //{
+ // msg = "获取成功",
+ // success = permissions.dataCount >= 0,
+ // response = permissions
+ //};
+
+ return permissions.dataCount >= 0 ? Success(permissions, "获取成功") : Failed>("获取失败");
+
+ }
+
+ ///
+ /// 查询树形 Table
+ ///
+ /// 父节点
+ /// 关键字
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task>> GetTreeTable(long f = 0, string key = "")
+ {
+ List permissions = new List();
+ var apiList = await _moduleServices.Query(d => d.IsDeleted == false);
+ var permissionsList = await _permissionServices.Query(d => d.IsDeleted == false);
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ if (key != "")
+ {
+ permissions = permissionsList.Where(a => a.Name.Contains(key)).OrderBy(a => a.OrderSort).ToList();
+ }
+ else
+ {
+ permissions = permissionsList.Where(a => a.Pid == f).OrderBy(a => a.OrderSort).ToList();
+ }
+
+ foreach (var item in permissions)
+ {
+ List pidarr = new() { };
+ var parent = permissionsList.FirstOrDefault(d => d.Id == item.Pid);
+
+ while (parent != null)
+ {
+ pidarr.Add(parent.Id);
+ parent = permissionsList.FirstOrDefault(d => d.Id == parent.Pid);
+ }
+
+ //item.PidArr = pidarr.OrderBy(d => d).Distinct().ToList();
+
+ pidarr.Reverse();
+ pidarr.Insert(0, 0);
+ item.PidArr = pidarr;
+
+ item.MName = apiList.FirstOrDefault(d => d.Id == item.Mid)?.LinkUrl;
+ item.hasChildren = permissionsList.Where(d => d.Pid == item.Id).Any();
+ }
+
+
+ //return new MessageModel>()
+ //{
+ // msg = "获取成功",
+ // success = true,
+ // response = permissions
+ //};
+ return Success(permissions, "获取成功");
+ }
+
+ ///
+ /// 添加一个菜单
+ ///
+ ///
+ ///
+ // POST: api/User
+ [HttpPost]
+ public async Task> Post([FromBody] Permission permission)
+ {
+ //var data = new MessageModel();
+
+ permission.CreateId = _user.ID;
+ permission.CreateBy = _user.Name;
+
+ var id = (await _permissionServices.Add(permission));
+ //data.success = id > 0;
+ //if (data.success)
+ //{
+ // data.response = id.ObjToString();
+ // data.msg = "添加成功";
+ //}
+
+
+ return id > 0 ? Success(id.ObjToString(), "添加成功") : Failed("添加失败");
+ }
+
+ ///
+ /// 保存菜单权限分配
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task> Assign([FromBody] AssignView assignView)
+ {
+ if (assignView.rid > 0)
+ {
+ //开启事务
+ try
+ {
+ var old_rmps = await _roleModulePermissionServices.Query(d => d.RoleId == assignView.rid);
+
+ _unitOfWorkManage.BeginTran();
+ await _permissionServices.Db.Deleteable(t => t.RoleId == assignView.rid).ExecuteCommandAsync();
+ var permissions = await _permissionServices.Query(d => d.IsDeleted == false);
+
+ List new_rmps = new List();
+ var nowTime = _permissionServices.Db.GetDate();
+ foreach (var item in assignView.pids)
+ {
+ var moduleid = permissions.Find(p => p.Id == item)?.Mid;
+ var find_old_rmps = old_rmps.Find(p => p.PermissionId == item);
+
+ RoleModulePermission roleModulePermission = new RoleModulePermission()
+ {
+ IsDeleted = false,
+ RoleId = assignView.rid,
+ ModuleId = moduleid.ObjToLong(),
+ PermissionId = item,
+ CreateId = find_old_rmps == null ? _user.ID : find_old_rmps.CreateId,
+ CreateBy = find_old_rmps == null ? _user.Name : find_old_rmps.CreateBy,
+ CreateTime = find_old_rmps == null ? nowTime : find_old_rmps.CreateTime,
+ ModifyId = _user.ID,
+ ModifyBy = _user.Name,
+ ModifyTime = nowTime
+
+ };
+ new_rmps.Add(roleModulePermission);
+ }
+ if (new_rmps.Count > 0) await _roleModulePermissionServices.Add(new_rmps);
+ _unitOfWorkManage.CommitTran();
+ }
+ catch (Exception)
+ {
+ _unitOfWorkManage.RollbackTran();
+ throw;
+ }
+ _requirement.Permissions.Clear();
+ return Success("保存成功");
+ }
+ else
+ {
+ return Failed("请选择要操作的角色");
+ }
+ }
+
+
+ ///
+ /// 获取菜单树
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ public async Task> GetPermissionTree(long pid = 0, bool needbtn = false)
+ {
+ //var data = new MessageModel();
+
+ var permissions = await _permissionServices.Query(d => d.IsDeleted == false);
+ var permissionTrees = (from child in permissions
+ where child.IsDeleted == false
+ orderby child.Id
+ select new PermissionTree
+ {
+ value = child.Id,
+ label = child.Name,
+ Pid = child.Pid,
+ isbtn = child.IsButton,
+ order = child.OrderSort,
+ }).ToList();
+ PermissionTree rootRoot = new PermissionTree
+ {
+ value = 0,
+ Pid = 0,
+ label = "根节点"
+ };
+
+ permissionTrees = permissionTrees.OrderBy(d => d.order).ToList();
+
+
+ RecursionHelper.LoopToAppendChildren(permissionTrees, rootRoot, pid, needbtn);
+
+ //data.success = true;
+ //if (data.success)
+ //{
+ // data.response = rootRoot;
+ // data.msg = "获取成功";
+ //}
+
+ return Success(rootRoot, "获取成功");
+ //return data;
+ }
+
+ ///
+ /// 获取路由树
+ ///
+ ///
+ ///
+ [HttpGet]
+ public async Task> GetNavigationBar(long uid)
+ {
+
+ var data = new MessageModel();
+
+ long uidInHttpcontext1 = 0;
+ var roleIds = new List();
+ // ids4和jwt切换
+ if (Permissions.IsUseIds4)
+ {
+ // ids4
+ uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == ClaimTypes.NameIdentifier
+ select item.Value).FirstOrDefault().ObjToLong();
+ if (!(uidInHttpcontext1 > 0))
+ {
+ uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == "sub"
+ select item.Value).FirstOrDefault().ObjToLong();
+ }
+ roleIds = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == ClaimTypes.Role
+ select item.Value.ObjToLong()).ToList();
+ if (!roleIds.Any())
+ {
+ roleIds = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == "role"
+ select item.Value.ObjToLong()).ToList();
+ }
+ }
+ else
+ {
+ // jwt
+ uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong();
+ roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList();
+ }
+
+
+ if (uid > 0 && uid == uidInHttpcontext1)
+ {
+ if (roleIds.Any())
+ {
+ var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))).Select(d => d.PermissionId.ObjToLong()).Distinct();
+ if (pids.Any())
+ {
+ var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id))).OrderBy(c => c.OrderSort);
+ var temp = rolePermissionMoudles.ToList().Find(t => t.Id == 87);
+ var permissionTrees = (from child in rolePermissionMoudles
+ where child.IsDeleted == false
+ orderby child.Id
+ select new NavigationBar
+ {
+ id = child.Id,
+ name = child.Name,
+ pid = child.Pid,
+ order = child.OrderSort,
+ path = child.Code,
+ iconCls = child.Icon,
+ Func = child.Func,
+ IsHide = child.IsHide.ObjToBool(),
+ IsButton = child.IsButton.ObjToBool(),
+ meta = new NavigationBarMeta
+ {
+ icon = child.IconNew,
+ requireAuth = true,
+ title = child.Name,
+ NoTabPage = child.IsHide.ObjToBool(),
+ keepAlive = child.IskeepAlive.ObjToBool()
+ }
+ }).ToList();
+
+
+ NavigationBar rootRoot = new NavigationBar()
+ {
+ id = 0,
+ pid = 0,
+ order = 0,
+ name = "根节点",
+ path = "",
+ iconCls = "",
+ meta = new NavigationBarMeta(),
+
+ };
+
+ permissionTrees = permissionTrees.OrderBy(d => d.order).ToList();
+ RecursionHelper.LoopNaviBarAppendChildren(permissionTrees, rootRoot);
+
+ data.success = true;
+ if (data.success)
+ {
+ data.response = rootRoot;
+ data.msg = "获取成功";
+ }
+ }
+ }
+ }
+ return data;
+ }
+
+ ///
+ /// 获取路由树
+ ///
+ ///
+ ///
+ [HttpGet]
+ public async Task>> GetNavigationBarPro(long uid)
+ {
+ var data = new MessageModel>();
+
+ long uidInHttpcontext1 = 0;
+ var roleIds = new List();
+ // ids4和jwt切换
+ if (Permissions.IsUseIds4)
+ {
+ // ids4
+ uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == ClaimTypes.NameIdentifier
+ select item.Value).FirstOrDefault().ObjToLong();
+ if (!(uidInHttpcontext1 > 0))
+ {
+ uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == "sub"
+ select item.Value).FirstOrDefault().ObjToLong();
+ }
+ roleIds = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == ClaimTypes.Role
+ select item.Value.ObjToLong()).ToList();
+ if (!roleIds.Any())
+ {
+ roleIds = (from item in _httpContext.HttpContext.User.Claims
+ where item.Type == "role"
+ select item.Value.ObjToLong()).ToList();
+ }
+ }
+ else
+ {
+ // jwt
+ uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong();
+ roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList();
+ }
+
+ if (uid > 0 && uid == uidInHttpcontext1)
+ {
+ if (roleIds.Any())
+ {
+ var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId)))
+ .Select(d => d.PermissionId.ObjToLong()).Distinct();
+ if (pids.Any())
+ {
+ var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id) && d.IsButton == false)).OrderBy(c => c.OrderSort);
+ var permissionTrees = (from item in rolePermissionMoudles
+ where item.IsDeleted == false
+ orderby item.Id
+ select new NavigationBarPro
+ {
+ id = item.Id,
+ name = item.Name,
+ parentId = item.Pid,
+ order = item.OrderSort,
+ path = item.Code == "-" ? item.Name.GetTotalPingYin().FirstOrDefault() : (item.Code == "/" ? "/dashboard/workplace" : item.Code),
+ component = item.Pid == 0 ? (item.Code == "/" ? "dashboard/Workplace" : "RouteView") : item.Code?.TrimStart('/'),
+ iconCls = item.Icon,
+ Func = item.Func,
+ IsHide = item.IsHide.ObjToBool(),
+ IsButton = item.IsButton.ObjToBool(),
+ meta = new NavigationBarMetaPro
+ {
+ show = true,
+ title = item.Name,
+ icon = "user"//item.Icon
+ }
+ }).ToList();
+
+ permissionTrees = permissionTrees.OrderBy(d => d.order).ToList();
+
+ data.success = true;
+ if (data.success)
+ {
+ data.response = permissionTrees;
+ data.msg = "获取成功";
+ }
+ }
+ }
+ }
+ return data;
+ }
+
+ ///
+ /// 通过角色获取菜单
+ ///
+ ///
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task> GetPermissionIdByRoleId(long rid = 0)
+ {
+ //var data = new MessageModel();
+
+ var rmps = await _roleModulePermissionServices.Query(d => d.IsDeleted == false && d.RoleId == rid);
+ var permissionTrees = (from child in rmps
+ orderby child.Id
+ select child.PermissionId.ObjToLong()).ToList();
+
+ var permissions = await _permissionServices.Query(d => d.IsDeleted == false);
+ List assignbtns = new List();
+
+ foreach (var item in permissionTrees)
+ {
+ var pername = permissions.FirstOrDefault(d => d.IsButton && d.Id == item)?.Name;
+ if (!string.IsNullOrEmpty(pername))
+ {
+ //assignbtns.Add(pername + "_" + item);
+ assignbtns.Add(item.ObjToString());
+ }
+ }
+
+ //data.success = true;
+ //if (data.success)
+ //{
+ // data.response = new AssignShow()
+ // {
+ // permissionids = permissionTrees,
+ // assignbtns = assignbtns,
+ // };
+ // data.msg = "获取成功";
+ //}
+
+ return Success(new AssignShow()
+ {
+ permissionids = permissionTrees,
+ assignbtns = assignbtns,
+ }, "获取成功");
+
+ //return data;
+ }
+
+ ///
+ /// 更新菜单
+ ///
+ ///
+ ///
+ // PUT: api/User/5
+ [HttpPut]
+ public async Task> Put([FromBody] Permission permission)
+ {
+ var data = new MessageModel();
+ if (permission != null && permission.Id > 0)
+ {
+ data.success = await _permissionServices.Update(permission);
+ await _roleModulePermissionServices.UpdateModuleId(permission.Id, permission.Mid);
+ if (data.success)
+ {
+ data.msg = "更新成功";
+ data.response = permission?.Id.ObjToString();
+ }
+ }
+
+ return data;
+ }
+
+ ///
+ /// 删除菜单
+ ///
+ ///
+ ///
+ // DELETE: api/ApiWithActions/5
+ [HttpDelete]
+ public async Task> Delete(long id)
+ {
+ var data = new MessageModel();
+ if (id > 0)
+ {
+ var userDetail = await _permissionServices.QueryById(id);
+ userDetail.IsDeleted = true;
+ data.success = await _permissionServices.Update(userDetail);
+ if (data.success)
+ {
+ data.msg = "删除成功";
+ data.response = userDetail?.Id.ObjToString();
+ }
+ }
+
+ return data;
+ }
+
+ ///
+ /// 导入多条菜单信息
+ ///
+ ///
+ ///
+ // POST: api/User
+ [HttpPost]
+ public async Task> BatchPost([FromBody] List permissions)
+ {
+ var data = new MessageModel();
+ string ids = string.Empty;
+ int sucCount = 0;
+
+ for (int i = 0; i < permissions.Count; i++)
+ {
+ var permission = permissions[i];
+ if (permission != null)
+ {
+ permission.CreateId = _user.ID;
+ permission.CreateBy = _user.Name;
+ ids += (await _permissionServices.Add(permission));
+ sucCount++;
+ }
+ }
+
+ data.success = ids.IsNotEmptyOrNull();
+ if (data.success)
+ {
+ data.response = ids;
+ data.msg = $"{sucCount}条数据添加成功";
+ }
+
+ return data;
+ }
+
+ ///
+ /// 系统接口菜单同步接口
+ ///
+ ///
+ [HttpGet]
+ public async Task>> MigratePermission(string action = "", string token = "", string gatewayPrefix = "", string swaggerDomain = "", string controllerName = "", long pid = 0, bool isAction = false)
+ {
+ var data = new MessageModel>();
+ if (controllerName.IsNullOrEmpty())
+ {
+ data.msg = "必须填写要迁移的所属接口的控制器名称";
+ return data;
+ }
+
+ controllerName = controllerName.TrimEnd('/').ToLower();
+
+ gatewayPrefix = gatewayPrefix.Trim();
+ swaggerDomain = swaggerDomain.Trim();
+ controllerName = controllerName.Trim();
+
+ using var client = _httpClientFactory.CreateClient();
+ var Configuration = swaggerDomain.IsNotEmptyOrNull() ? swaggerDomain : AppSettings.GetValue("SystemCfg:Domain");
+ var url = $"{Configuration}/swagger/V2/swagger.json";
+ if (Configuration.IsNullOrEmpty())
+ {
+ data.msg = "Swagger.json在线文件域名不能为空";
+ return data;
+ }
+ if (token.IsNullOrEmpty()) token = Request.Headers.Authorization;
+ token = token.Trim();
+ client.DefaultRequestHeaders.Add("Authorization", $"{token}");
+
+ var response = await client.GetAsync(url);
+ var body = await response.Content.ReadAsStringAsync();
+
+ var resultJObj = (JObject)JsonConvert.DeserializeObject(body);
+ var paths = resultJObj["paths"].ObjToString();
+ var pathsJObj = (JObject)JsonConvert.DeserializeObject(paths);
+
+ List permissions = new List();
+ foreach (JProperty jProperty in pathsJObj.Properties())
+ {
+ var apiPath = gatewayPrefix + jProperty.Name.ToLower();
+ if (action.IsNotEmptyOrNull())
+ {
+ action = action.Trim();
+ if (!apiPath.Contains(action.ToLower()))
+ {
+ continue;
+ }
+ }
+ string httpmethod = "";
+ if (jProperty.Value.ToString().ToLower().Contains("get"))
+ {
+ httpmethod = "get";
+ }
+ else if (jProperty.Value.ToString().ToLower().Contains("post"))
+ {
+ httpmethod = "post";
+ }
+ else if (jProperty.Value.ToString().ToLower().Contains("put"))
+ {
+ httpmethod = "put";
+ }
+ else if (jProperty.Value.ToString().ToLower().Contains("delete"))
+ {
+ httpmethod = "delete";
+ }
+
+ var summary = jProperty.Value?.SelectToken($"{httpmethod}.summary")?.ObjToString() ?? "";
+
+ var subIx = summary.IndexOf("(Auth");
+ if (subIx >= 0)
+ {
+ summary = summary.Substring(0, subIx);
+ }
+
+ permissions.Add(new Permission()
+ {
+ Code = " ",
+ Name = summary,
+ IsButton = true,
+ IsHide = false,
+ Enabled = true,
+ CreateTime = DateTime.Now,
+ IsDeleted = false,
+ Pid = pid,
+ Module = new Modules()
+ {
+ LinkUrl = apiPath ?? "",
+ Name = summary,
+ Enabled = true,
+ CreateTime = DateTime.Now,
+ ModifyTime = DateTime.Now,
+ IsDeleted = false,
+ }
+ });
+ }
+
+ var modulesList = (await _moduleServices.Query(d => d.IsDeleted == false && d.LinkUrl != null)).Select(d => d.LinkUrl.ToLower()).ToList();
+ permissions = permissions.Where(d => !modulesList.Contains(d.Module.LinkUrl.ToLower()) && d.Module.LinkUrl.Contains($"/{controllerName}/")).ToList();
+
+
+ if (isAction)
+ {
+ foreach (var item in permissions)
+ {
+ List modules = await _moduleServices.Query(d => d.LinkUrl != null && d.LinkUrl.ToLower() == item.Module.LinkUrl);
+ if (!modules.Any())
+ {
+ var mid = await _moduleServices.Add(item.Module);
+ if (mid > 0)
+ {
+ item.Mid = mid;
+ var permissionid = await _permissionServices.Add(item);
+ }
+
+ }
+ }
+ data.msg = "同步完成";
+ }
+
+ data.response = permissions;
+ data.status = 200;
+ data.success = isAction;
+
+ return data;
+ }
+ }
+
+ public class AssignView
+ {
+ public List pids { get; set; }
+ public long rid { get; set; }
+ }
+ public class AssignShow
+ {
+ public List permissionids { get; set; }
+ public List assignbtns { get; set; }
+ }
+
+}
diff --git a/Blog.Core.Api/Controllers/RoleController.cs b/Blog.Core.Api/Controllers/RoleController.cs
new file mode 100644
index 00000000..0b93e943
--- /dev/null
+++ b/Blog.Core.Api/Controllers/RoleController.cs
@@ -0,0 +1,139 @@
+using Blog.Core.Common.HttpContextUser;
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Controllers
+{
+ ///
+ /// 角色管理
+ ///
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ [Authorize(Permissions.Name)]
+ public class RoleController : BaseApiController
+ {
+ readonly IRoleServices _roleServices;
+ readonly IUser _user;
+
+
+ public RoleController(IRoleServices roleServices, IUser user)
+ {
+ _roleServices = roleServices;
+ _user = user;
+ }
+
+ ///
+ /// 获取全部角色
+ ///
+ ///
+ ///
+ ///
+ // GET: api/User
+ [HttpGet]
+ public async Task>> Get(int page = 1, string key = "")
+ {
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+
+ int intPageSize = 50;
+
+ var data = await _roleServices.QueryPage(a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key)), page, intPageSize, " Id desc ");
+
+ //return new MessageModel>()
+ //{
+ // msg = "获取成功",
+ // success = data.dataCount >= 0,
+ // response = data
+ //};
+
+ return Success(data, "获取成功");
+
+ }
+
+ // GET: api/User/5
+ [HttpGet("{id}")]
+ public string Get(string id)
+ {
+ return "value";
+ }
+
+ ///
+ /// 添加角色
+ ///
+ ///
+ ///
+ // POST: api/User
+ [HttpPost]
+ public async Task> Post([FromBody] Role role)
+ {
+ role.CreateId = _user.ID;
+ role.CreateBy = _user.Name;
+ var id = (await _roleServices.Add(role));
+ return id > 0 ? Success(id.ObjToString(), "添加成功") : Failed("添加失败");
+
+ }
+
+ ///
+ /// 更新角色
+ ///
+ ///
+ ///
+ // PUT: api/User/5
+ [HttpPut]
+ public async Task> Put([FromBody] Role role)
+ {
+ if (role == null || role.Id <= 0)
+ return Failed("缺少参数");
+
+ return await _roleServices.Update(role) ? Success(role?.Id.ObjToString(),"更新成功") : Failed("更新失败");
+
+ //var data = new MessageModel();
+ //if (role != null && role.Id > 0)
+ //{
+ // data.success = await _roleServices.Update(role);
+ // if (data.success)
+ // {
+ // data.msg = "更新成功";
+ // data.response = role?.Id.ObjToString();
+ // }
+ //}
+ //return data;
+ }
+
+ ///
+ /// 删除角色
+ ///
+ ///
+ ///
+ // DELETE: api/ApiWithActions/5
+ [HttpDelete]
+ public async Task> Delete(long id)
+ {
+
+ var data = new MessageModel();
+ //if (id > 0)
+ //{
+ // var userDetail = await _roleServices.QueryById(id);
+ // userDetail.IsDeleted = true;
+ // data.success = await _roleServices.Update(userDetail);
+ // if (data.success)
+ // {
+ // data.msg = "删除成功";
+ // data.response = userDetail?.Id.ObjToString();
+ // }
+ //}
+ //return data;
+
+ if (id <= 0) return Failed();
+ var userDetail = await _roleServices.QueryById(id);
+ if (userDetail == null) return Success(null,"角色不存在");
+ userDetail.IsDeleted = true;
+ return await _roleServices.Update(userDetail) ? Success(userDetail?.Id.ObjToString(), "删除成功") : Failed();
+ }
+ }
+}
diff --git a/Blog.Core.Api/Controllers/SignalRTestController.cs b/Blog.Core.Api/Controllers/SignalRTestController.cs
new file mode 100644
index 00000000..16ba47f0
--- /dev/null
+++ b/Blog.Core.Api/Controllers/SignalRTestController.cs
@@ -0,0 +1,49 @@
+using Blog.Core.Controllers;
+using Blog.Core.Hubs;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.SignalR;
+
+namespace Blog.Core.Api.Controllers;
+
+///
+/// SignalR测试
+///
+[Route("api/[controller]/[action]")]
+[ApiController]
+[Authorize]
+public class SignalRTestController : BaseApiController
+{
+ private readonly IHubContext _hubContext;
+
+ public SignalRTestController(IHubContext hubContext)
+ {
+ _hubContext = hubContext;
+ }
+
+ ///
+ /// 向指定用户发送消息
+ ///
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task SendMessageToUser(string user, string message)
+ {
+ await _hubContext.Clients.Group(user).ReceiveMessage(user, message);
+ return Ok();
+ }
+
+ ///
+ /// 向指定角色发送消息
+ ///
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task SendMessageToRole(string role, string message)
+ {
+ await _hubContext.Clients.Group(role).ReceiveMessage(role, message);
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/SplitDemoController.cs b/Blog.Core.Api/Controllers/SplitDemoController.cs
new file mode 100644
index 00000000..f625b202
--- /dev/null
+++ b/Blog.Core.Api/Controllers/SplitDemoController.cs
@@ -0,0 +1,199 @@
+using Blog.Core.IServices;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Blog.Core.Repository.UnitOfWorks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.Linq.Expressions;
+
+namespace Blog.Core.Api.Controllers
+{
+ ///
+ /// 分表demo
+ ///
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ [Authorize(Permissions.Name)]
+ public class SplitDemoController : ControllerBase
+ {
+ readonly ISplitDemoServices splitDemoServices;
+ readonly IUnitOfWorkManage unitOfWorkManage;
+ public SplitDemoController(ISplitDemoServices _splitDemoServices, IUnitOfWorkManage _unitOfWorkManage)
+ {
+ splitDemoServices = _splitDemoServices;
+ unitOfWorkManage = _unitOfWorkManage;
+ }
+
+ ///
+ /// 分页获取数据
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task>> Get(DateTime beginTime, DateTime endTime, int page = 1, string key = "", int pageSize = 10)
+ {
+ if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
+ {
+ key = "";
+ }
+ Expression> whereExpression = a => (a.Name != null && a.Name.Contains(key));
+ var data = await splitDemoServices.QueryPageSplit(whereExpression, beginTime, endTime, page, pageSize, " Id desc ");
+ return MessageModel>.Message(data.dataCount >= 0, "获取成功", data);
+ }
+
+ ///
+ /// 根据ID获取信息
+ ///
+ ///
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task> GetById(long id)
+ {
+ var data = new MessageModel();
+ var model = await splitDemoServices.QueryByIdSplit(id);
+ if (model != null)
+ {
+ return MessageModel.Success("获取成功", model);
+ }
+ else
+ {
+ return MessageModel.Fail("获取失败");
+ }
+ }
+
+ ///
+ /// 添加一条测试数据
+ ///
+ ///
+ ///
+ [HttpPost]
+ [AllowAnonymous]
+ public async Task> Post([FromBody] SplitDemo splitDemo)
+ {
+ var data = new MessageModel();
+ //unitOfWorkManage.BeginTran();
+ var id = (await splitDemoServices.AddSplit(splitDemo));
+ data.success = (id == null ? false : true);
+ try
+ {
+ if (data.success)
+ {
+ data.response = id.FirstOrDefault().ToString();
+ data.msg = "添加成功";
+ }
+ else
+ {
+ data.msg = "添加失败";
+ }
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ finally
+ {
+ //if (data.success)
+ // unitOfWorkManage.CommitTran();
+ //else
+ // unitOfWorkManage.RollbackTran();
+ }
+ return data;
+ }
+
+ ///
+ /// 修改一条测试数据
+ ///
+ ///
+ ///
+ [HttpPut]
+ [AllowAnonymous]
+ public async Task> Put([FromBody] SplitDemo splitDemo)
+ {
+ var data = new MessageModel();
+ if (splitDemo != null && splitDemo.Id > 0)
+ {
+ unitOfWorkManage.BeginTran();
+ data.success = await splitDemoServices.UpdateSplit(splitDemo, splitDemo.CreateTime);
+ try
+ {
+ if (data.success)
+ {
+ data.msg = "修改成功";
+ data.response = splitDemo?.Id.ObjToString();
+ }
+ else
+ {
+ data.msg = "修改失败";
+ }
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ finally
+ {
+ if (data.success)
+ unitOfWorkManage.CommitTran();
+ else
+ unitOfWorkManage.RollbackTran();
+ }
+ }
+ return data;
+ }
+
+ ///
+ /// 根据id删除数据
+ ///
+ ///
+ ///
+ [HttpDelete]
+ [AllowAnonymous]
+ public async Task> Delete(long id)
+ {
+ var data = new MessageModel();
+
+ var model = await splitDemoServices.QueryByIdSplit(id);
+ if (model != null)
+ {
+ unitOfWorkManage.BeginTran();
+ data.success = await splitDemoServices.DeleteSplit(model,model.CreateTime);
+ try
+ {
+ data.response = id.ObjToString();
+ if (data.success)
+ {
+ data.msg = "删除成功";
+ }
+ else
+ {
+ data.msg = "删除失败";
+ }
+
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ finally
+ {
+ if (data.success)
+ unitOfWorkManage.CommitTran();
+ else
+ unitOfWorkManage.RollbackTran();
+ }
+ }
+ else
+ {
+ data.msg = "不存在";
+ }
+ return data;
+
+ }
+ }
+}
diff --git a/Blog.Core.Api/Controllers/SqlSugarTestController.cs b/Blog.Core.Api/Controllers/SqlSugarTestController.cs
new file mode 100644
index 00000000..f43e32bf
--- /dev/null
+++ b/Blog.Core.Api/Controllers/SqlSugarTestController.cs
@@ -0,0 +1,61 @@
+using System.Text;
+using Blog.Core.Common.DB.Extension;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+
+namespace Blog.Core.Api.Controllers;
+
+///
+/// SqlSugar 相关测试
+///
+[Route("api/[controller]/[action]")]
+[ApiController]
+[AllowAnonymous]
+public class SqlSugarTestController(ISqlSugarClient db) : BaseApiController
+{
+ ///
+ /// 测试建表后,SqlSugar缓存
+ ///
+ ///
+ [HttpGet]
+ public MessageModel ClearDbTableCache()
+ {
+ var tableName = "BlogArticle_Test";
+
+ //先删除表
+ try
+ {
+ db.DbMaintenance.DropTable(tableName);
+ db.ClearDbTableCache();
+ }
+ catch
+ {
+ //Ignore
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ //提前检查表是否存在,测试缓存
+ sb.AppendLine($"表{tableName} 是否存在:{db.DbMaintenance.IsAnyTable(tableName)}");
+
+ //创建表
+ db.CodeFirst.As(tableName).InitTables();
+ sb.AppendLine($"表{tableName} 创建成功");
+
+ //检查表是否存在
+ sb.AppendLine($"表{tableName} 是否存在:{db.DbMaintenance.IsAnyTable(tableName)}");
+
+ //清除缓存
+ db.ClearDbTableCache();
+ sb.AppendLine($"清除缓存后");
+
+ //检查表是否存在
+ sb.AppendLine($"表{tableName} 是否存在:{db.DbMaintenance.IsAnyTable(tableName)}");
+
+ return Success(sb.ToString());
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/Systems/CacheManageController.cs b/Blog.Core.Api/Controllers/Systems/CacheManageController.cs
new file mode 100644
index 00000000..ef276c17
--- /dev/null
+++ b/Blog.Core.Api/Controllers/Systems/CacheManageController.cs
@@ -0,0 +1,75 @@
+using Blog.Core.Common.Caches;
+using Blog.Core.Common.Caches.Interface;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blog.Core.Api.Controllers.Systems;
+
+///
+/// 缓存管理
+///
+[Route("api/Systems/[controller]")]
+[ApiController]
+[Authorize(Permissions.Name)]
+public class CacheManageController(ICaching caching) : BaseApiController
+{
+ ///
+ /// 获取全部缓存
+ ///
+ ///
+ [HttpGet]
+ public MessageModel> Get()
+ {
+ return Success(caching.GetAllCacheKeys());
+ }
+
+ ///
+ /// 获取缓存
+ ///
+ ///
+ [HttpGet("{key}")]
+ public async Task> Get(string key)
+ {
+ return Success(await caching.GetStringAsync(key));
+ }
+
+ ///
+ /// 新增
+ ///
+ ///
+ [HttpPost]
+ public async Task Post([FromQuery] string key, [FromQuery] string value, [FromQuery] int? expire)
+ {
+ if (expire.HasValue)
+ await caching.SetStringAsync(key, value, TimeSpan.FromMilliseconds(expire.Value));
+ else
+ await caching.SetStringAsync(key, value);
+
+ return Success();
+ }
+
+ ///
+ /// 删除全部缓存
+ ///
+ ///
+ [HttpDelete]
+ public MessageModel Delete()
+ {
+ caching.RemoveAll();
+ return Success();
+ }
+
+ ///
+ /// 删除缓存
+ ///
+ ///
+ [Route("{key}")]
+ [HttpDelete]
+ public async Task Delete(string key)
+ {
+ await caching.RemoveAsync(key);
+ return Success();
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/Systems/DataBaseController.cs b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs
new file mode 100644
index 00000000..1f7b3089
--- /dev/null
+++ b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs
@@ -0,0 +1,192 @@
+using System.Diagnostics.CodeAnalysis;
+using Blog.Core.Common;
+using Blog.Core.Common.DB;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Blog.Core.Model.Systems.DataBase;
+using Blog.Core.Model.Tenants;
+using Mapster;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+
+namespace Blog.Core.Api.Controllers.Systems;
+
+///
+/// 数据库管理
+///
+[Route("api/Systems/[controller]/[action]")]
+[ApiController]
+[Authorize(Permissions.Name)]
+public class DataBaseController : BaseApiController
+{
+ private readonly ISqlSugarClient _db;
+
+ public DataBaseController(ISqlSugarClient db)
+ {
+ _db = db;
+ }
+
+ [return: NotNull]
+ private ISqlSugarClient GetTenantDb(string configId)
+ {
+ if (!_db.AsTenant().IsAnyConnection(configId))
+ {
+ var tenant = _db.Queryable().WithCache()
+ .Where(s => s.TenantType == TenantTypeEnum.Db)
+ .Where(s => s.ConfigId == configId)
+ .First();
+ if (tenant != null)
+ {
+ _db.AsTenant().AddConnection(tenant.GetConnectionConfig());
+ }
+ }
+
+ var db = _db.AsTenant().GetConnectionScope(configId);
+ if (db is null)
+ {
+ throw new ApplicationException("无效的数据库配置");
+ }
+
+ return db;
+ }
+
+ ///
+ /// 获取库配置
+ ///
+ ///
+ [HttpGet]
+ public async Task>> GetAllConfig()
+ {
+ //增加多租户的连接
+ var allConfigs = new List(BaseDBConfig.AllConfigs);
+ var tenants = await _db.Queryable().WithCache()
+ .Where(s => s.TenantType == TenantTypeEnum.Db)
+ .ToListAsync();
+ if (tenants.Any())
+ {
+ allConfigs.AddRange(tenants.Select(tenant => tenant.GetConnectionConfig()));
+ }
+
+ var configs = await Task.FromResult(allConfigs);
+ return Success(configs.Adapt>());
+ }
+
+ ///
+ /// 获取表信息
+ ///
+ /// 配置Id
+ /// 读取类型
+ ///
+ [HttpGet]
+ public MessageModel> GetTableInfoList(string configId,
+ DataBaseReadType readType = DataBaseReadType.Db)
+ {
+ if (configId.IsNullOrEmpty())
+ {
+ configId = MainDb.CurrentDbConnId;
+ }
+
+ configId = configId.ToLower();
+
+ var provider = GetTenantDb(configId);
+ List data = null;
+ switch (readType)
+ {
+ case DataBaseReadType.Db:
+ data = provider.DbMaintenance.GetTableInfoList(false);
+ break;
+ case DataBaseReadType.Entity:
+ if (EntityUtility.TenantEntitys.TryGetValue(configId, out var types))
+ {
+ data = types.Select(s => provider.EntityMaintenance.GetEntityInfo(s))
+ .Select(s => new {Name = s.DbTableName, Description = s.TableDescription})
+ .Adapt>();
+ }
+
+ break;
+ }
+
+
+ return Success(data);
+ }
+
+ ///
+ /// 获取表字段
+ ///
+ /// 表名
+ /// ConfigId
+ /// 读取类型
+ ///
+ [HttpGet]
+ public MessageModel> GetColumnInfosByTableName(string tableName, string configId = null,
+ DataBaseReadType readType = DataBaseReadType.Db)
+ {
+ if (string.IsNullOrWhiteSpace(tableName))
+ return Failed>("表名不能为空");
+
+ if (configId.IsNullOrEmpty())
+ {
+ configId = MainDb.CurrentDbConnId;
+ }
+
+ configId = configId.ToLower();
+
+ List data = null;
+ var provider = GetTenantDb(configId);
+ switch (readType)
+ {
+ case DataBaseReadType.Db:
+ data = provider.DbMaintenance.GetColumnInfosByTableName(tableName, false)
+ .Adapt>();
+ break;
+ case DataBaseReadType.Entity:
+ if (EntityUtility.TenantEntitys.TryGetValue(configId, out var types))
+ {
+ var type = types.FirstOrDefault(s => s.Name == tableName);
+ data = provider.EntityMaintenance.GetEntityInfo(type).Columns.Adapt>();
+ }
+
+ break;
+ }
+
+
+ return Success(data);
+ }
+
+ ///
+ /// 编辑表备注
+ ///
+ ///
+ [HttpPut]
+ public MessageModel PutTableEditRemark([FromBody] EditTableInput input)
+ {
+ var provider = GetTenantDb(input.ConfigId);
+ if (provider.DbMaintenance.IsAnyTableRemark(input.TableName))
+ {
+ provider.DbMaintenance.DeleteTableRemark(input.TableName);
+ }
+
+ provider.DbMaintenance.AddTableRemark(input.TableName, input.Description);
+ return Success();
+ }
+
+ ///
+ /// 编辑列备注
+ ///
+ ///
+ [HttpPut]
+ public MessageModel PutColumnEditRemark([FromBody] EditColumnInput input)
+ {
+ var provider = GetTenantDb(input.ConfigId);
+ if (provider.DbMaintenance.IsAnyColumnRemark(input.DbColumnName, input.TableName))
+ {
+ provider.DbMaintenance.DeleteColumnRemark(input.DbColumnName, input.TableName);
+ }
+
+ provider.DbMaintenance.AddColumnRemark(input.DbColumnName, input.TableName, input.ColumnDescription);
+
+ return Success();
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs
new file mode 100644
index 00000000..37c84791
--- /dev/null
+++ b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs
@@ -0,0 +1,96 @@
+using Blog.Core.Common.DB.Extension;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Blog.Core.Model.Models.RootTkey;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using NetTaste;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
+using SqlSugar;
+
+namespace Blog.Core.Api.Controllers.Systems;
+
+///
+/// 动态建表 CURD
+///
+[Route("api/Systems/[controller]/[action]")]
+[ApiController]
+[Authorize(Permissions.Name)]
+public class DynamicCodeFirstController : BaseApiController
+{
+ private readonly ISqlSugarClient _db;
+
+ public DynamicCodeFirstController(ISqlSugarClient db)
+ {
+ _db = db;
+ }
+
+ ///
+ /// 动态type
+ ///
+ ///
+ private Type GetDynamicType()
+ {
+ return _db.DynamicBuilder().CreateClass("DynamicTestTable")
+ //{table} 占位符会自动替换成表名
+ .CreateIndex(new SugarIndexAttribute("idx_{table}_Code", "Code", OrderByType.Desc))
+ .CreateProperty("Id", typeof(int), new SugarColumn() {IsPrimaryKey = true, IsIdentity = true})
+ .CreateProperty("Code", typeof(string), new SugarColumn() {Length = 50})
+ .CreateProperty("Name", typeof(string), new SugarColumn() {Length = 50})
+ .WithCache()
+ .BuilderType();
+ }
+
+ ///
+ /// 动态type 继承BaseEntity
+ ///
+ ///
+ private Type GetDynamicType2()
+ {
+ return _db.DynamicBuilder().CreateClass("DynamicTestTable2", null, typeof(BaseEntity))
+ //{table} 占位符会自动替换成表名
+ .CreateIndex(new SugarIndexAttribute("idx_{table}_Code", "Code", OrderByType.Desc))
+ .CreateProperty("Code", typeof(string), new SugarColumn() {Length = 50})
+ .CreateProperty("Name", typeof(string), new SugarColumn() {Length = 50})
+ .WithCache()
+ .BuilderType();
+ }
+
+ ///
+ /// 测试建表
+ ///
+ ///
+ [HttpPost]
+ public MessageModel TestCreateTable()
+ {
+ var type = GetDynamicType();
+ _db.CodeFirst.InitTables(type);
+ return Success();
+ }
+
+ ///
+ /// 测试查询
+ ///
+ ///
+ [HttpGet]
+ public MessageModel