diff --git a/.gitignore b/.gitignore
index 8b53321..c1cb5bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,8 +18,5 @@ bld/
/Data
/Log
*.log
-*.htm
*.nuspec
*.nupkg
-/BinTest
-/BinUnitTest
diff --git a/NewLife.YuQue.sln b/NewLife.YuQue.sln
index 6584a84..4902483 100644
--- a/NewLife.YuQue.sln
+++ b/NewLife.YuQue.sln
@@ -16,7 +16,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\test.yml = .github\workflows\test.yml
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.YuQue", "NewLife.YuQue\NewLife.YuQue.csproj", "{CC0B8977-F646-4211-BC1F-CD2BCA28971D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.Yuque", "NewLife.YuQue\NewLife.Yuque.csproj", "{CC0B8977-F646-4211-BC1F-CD2BCA28971D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.YuqueWeb", "NewLife.YuQueWeb\NewLife.YuqueWeb.csproj", "{D7B1576C-2B4D-4733-A6A3-DAEFCA95B8B4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YqWeb", "YQWeb\YqWeb.csproj", "{271C5F95-20EF-43E8-A6E6-22FDCD1B06F6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -36,6 +40,14 @@ Global
{CC0B8977-F646-4211-BC1F-CD2BCA28971D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC0B8977-F646-4211-BC1F-CD2BCA28971D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC0B8977-F646-4211-BC1F-CD2BCA28971D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D7B1576C-2B4D-4733-A6A3-DAEFCA95B8B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D7B1576C-2B4D-4733-A6A3-DAEFCA95B8B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D7B1576C-2B4D-4733-A6A3-DAEFCA95B8B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D7B1576C-2B4D-4733-A6A3-DAEFCA95B8B4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {271C5F95-20EF-43E8-A6E6-22FDCD1B06F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {271C5F95-20EF-43E8-A6E6-22FDCD1B06F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {271C5F95-20EF-43E8-A6E6-22FDCD1B06F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {271C5F95-20EF-43E8-A6E6-22FDCD1B06F6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/NewLife.YuQue/Models/BookDetail.cs b/NewLife.YuQue/Models/BookDetail.cs
index 144752f..2f75c4d 100644
--- a/NewLife.YuQue/Models/BookDetail.cs
+++ b/NewLife.YuQue/Models/BookDetail.cs
@@ -48,7 +48,7 @@ namespace NewLife.YuQue.Models
[DataMember(Name = "likes_count")]
public Int32 Likes { get; set; }
- /// <summary> 订阅数量</summary>
+ /// <summary>订阅数量</summary>
[DataMember(Name = "watches_count")]
public Int32 Watches { get; set; }
diff --git a/NewLife.YuQue/NewLife.YuQue.csproj b/NewLife.YuQue/NewLife.YuQue.csproj
index b24b10b..3f70d17 100644
--- a/NewLife.YuQue/NewLife.YuQue.csproj
+++ b/NewLife.YuQue/NewLife.YuQue.csproj
@@ -11,7 +11,7 @@
<FileVersion>$(Version)</FileVersion>
<AssemblyVersion>$(VersionPrefix).*</AssemblyVersion>
<Deterministic>false</Deterministic>
- <OutputPath>..\..\Bin</OutputPath>
+ <OutputPath>..\Bin</OutputPath>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
diff --git a/NewLife.YuQue/YuqueClient.cs b/NewLife.YuQue/YuqueClient.cs
index c207f98..3a08d5d 100644
--- a/NewLife.YuQue/YuqueClient.cs
+++ b/NewLife.YuQue/YuqueClient.cs
@@ -754,7 +754,7 @@ namespace NewLife.YuQue
}
#endregion
- #region 属性
+ #region 辅助
/// <summary>性能追踪</summary>
public ITracer Tracer { get; set; }
diff --git a/NewLife.YuqueWeb/appsettings.Development.json b/NewLife.YuqueWeb/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/NewLife.YuqueWeb/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/NewLife.YuqueWeb/appsettings.json b/NewLife.YuqueWeb/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/NewLife.YuqueWeb/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/NewLife.YuqueWeb/Areas/Yuque/YuqueArea.cs b/NewLife.YuqueWeb/Areas/Yuque/YuqueArea.cs
new file mode 100644
index 0000000..39254bd
--- /dev/null
+++ b/NewLife.YuqueWeb/Areas/Yuque/YuqueArea.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel;
+using NewLife.Cube;
+
+namespace NewLife.YuqueWeb.Areas.Yuque;
+
+/// <summary>语雀管理区域注册</summary>
+[DisplayName("语雀管理")]
+[Menu(-2, true, Icon = "fa-tachometer")]
+public class YuqueArea : AreaBase
+{
+ /// <inheritdoc />
+ public YuqueArea() : base(nameof(YuqueArea).TrimEnd("Area")) { }
+}
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/Entity/Model.xml b/NewLife.YuqueWeb/Entity/Model.xml
new file mode 100644
index 0000000..49e087d
--- /dev/null
+++ b/NewLife.YuqueWeb/Entity/Model.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Tables xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://www.newlifex.com http://www.newlifex.com/Model2022.xsd" NameSpace="NewLife.YuQueWeb.Entity" ConnName="YuQue" Output="" BaseClass="Entity" Version="11.0.2022.0405" Document="https://www.yuque.com/smartstone/xcode/model" xmlns="http://www.newlifex.com/Model2022.xsd">
+ <Table Name="Book" Description="知识库。管理知识库">
+ <Columns>
+ <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+ <Column Name="Code" DataType="String" Description="编码。路径唯一标识,默认取Slug" />
+ <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" />
+ <Column Name="Type" DataType="String" Description="类型" />
+ <Column Name="Enable" DataType="Boolean" Description="启用" />
+ <Column Name="UserName" DataType="String" Description="用户" />
+ <Column Name="Docs" DataType="Int32" Description="文章数" />
+ <Column Name="Likes" DataType="Int32" Description="点赞数" />
+ <Column Name="Watches" DataType="Int32" Description="订阅数" />
+ <Column Name="Sync" DataType="Boolean" Description="同步。是否自动同步远程内容" />
+ <Column Name="Slug" DataType="String" Description="路径" />
+ <Column Name="Namespace" DataType="String" Description="全路径" />
+ <Column Name="SyncTime" DataType="DateTime" Description="同步时间。最后一次同步数据的时间" />
+ <Column Name="CreateUser" DataType="String" Description="创建者" Model="False" />
+ <Column Name="CreateUserID" DataType="Int32" Description="创建人" Model="False" />
+ <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
+ <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
+ <Column Name="UpdateUser" DataType="String" Description="更新者" Model="False" />
+ <Column Name="UpdateUserID" DataType="Int32" Description="更新人" Model="False" />
+ <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" />
+ <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" />
+ <Column Name="Remark" DataType="String" Length="500" Description="备注" />
+ </Columns>
+ <Indexes>
+ <Index Columns="Code" Unique="True" />
+ <Index Columns="Name" />
+ </Indexes>
+ </Table>
+ <Table Name="Document" Description="文档。文档内容">
+ <Columns>
+ <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+ <Column Name="Code" DataType="String" Description="编码。路径唯一标识,默认取Slug" />
+ <Column Name="Title" DataType="String" Master="True" Nullable="False" Description="标题" />
+ <Column Name="BookId" DataType="Int32" Description="知识库" />
+ <Column Name="Enable" DataType="Boolean" Description="启用" />
+ <Column Name="UserName" DataType="String" Description="用户" />
+ <Column Name="Format" DataType="String" Description="格式" />
+ <Column Name="Hits" DataType="Int32" Description="点击量" />
+ <Column Name="Likes" DataType="Int32" Description="点赞数" />
+ <Column Name="Comments" DataType="Int32" Description="评论数" />
+ <Column Name="Body" DataType="String" Length="-1" Description="正文。Markdown格式" />
+ <Column Name="BodyHtml" DataType="String" Length="-1" Description="HTML正文" />
+ <Column Name="ContentUpdateTime" DataType="DateTime" Description="内容更新时间" />
+ <Column Name="PublishTime" DataType="DateTime" Description="发布时间" />
+ <Column Name="FirstPublishTime" DataType="DateTime" Description="首次发布" />
+ <Column Name="WordCount" DataType="Int32" Description="单词数" />
+ <Column Name="Cover" DataType="String" Description="封面" />
+ <Column Name="CreateUser" DataType="String" Description="创建者" Model="False" />
+ <Column Name="CreateUserID" DataType="Int32" Description="创建人" Model="False" />
+ <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
+ <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
+ <Column Name="UpdateUser" DataType="String" Description="更新者" Model="False" />
+ <Column Name="UpdateUserID" DataType="Int32" Description="更新人" Model="False" />
+ <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" />
+ <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" />
+ <Column Name="Remark" DataType="String" Length="500" Description="备注" />
+ </Columns>
+ <Indexes>
+ <Index Columns="Code" Unique="True" />
+ <Index Columns="BookId" />
+ <Index Columns="Title" />
+ </Indexes>
+ </Table>
+</Tables>
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/Entity/xcodetool.exe b/NewLife.YuqueWeb/Entity/xcodetool.exe
new file mode 100644
index 0000000..c3742e5
Binary files /dev/null and b/NewLife.YuqueWeb/Entity/xcodetool.exe differ
diff --git a/NewLife.YuqueWeb/Entity/YuQue.htm b/NewLife.YuqueWeb/Entity/YuQue.htm
new file mode 100644
index 0000000..20d9e6a
--- /dev/null
+++ b/NewLife.YuqueWeb/Entity/YuQue.htm
@@ -0,0 +1,598 @@
+<style>
+ table {
+ border-collapse: collapse;
+ border: 1px solid;
+ border-color: rgb(211, 202, 221);
+ }
+
+ table thead,
+ table tr {
+ border-top-width: 1px;
+ border-top-style: solid;
+ border-top-color: rgb(211, 202, 221);
+ }
+
+ table {
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-bottom-color: rgb(211, 202, 221);
+ }
+
+ table td,
+ table th {
+ padding: 5px 10px;
+ font-size: 14px;
+ font-family: Verdana;
+ color: rgb(95, 74, 121);
+ }
+
+ table tr:nth-child(even) {
+ background: rgb(223, 216, 232)
+ }
+
+ table tr:nth-child(odd) {
+ background: #FFF
+ }
+</style>
+<h3>知识库(Book)</h3>
+<table>
+ <thead>
+ <tr>
+ <th>名称</th>
+ <th>显示名</th>
+ <th>类型</th>
+ <th>长度</th>
+ <th>精度</th>
+ <th>主键</th>
+ <th>允许空</th>
+ <th>备注</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Id</td>
+ <td>编号</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td title="自增">AI</td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Code</td>
+ <td>编码</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td title="唯一索引">UQ</td>
+ <td></td>
+ <td>路径唯一标识,默认取Slug</td>
+ </tr>
+
+ <tr>
+ <td>Name</td>
+ <td>名称</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Type</td>
+ <td>类型</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Enable</td>
+ <td>启用</td>
+ <td>Boolean</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UserName</td>
+ <td>用户</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Docs</td>
+ <td>文章数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Likes</td>
+ <td>点赞数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Watches</td>
+ <td>订阅数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Sync</td>
+ <td>同步</td>
+ <td>Boolean</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td>是否自动同步远程内容</td>
+ </tr>
+
+ <tr>
+ <td>Slug</td>
+ <td>路径</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Namespace</td>
+ <td>全路径</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>SyncTime</td>
+ <td>同步时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>最后一次同步数据的时间</td>
+ </tr>
+
+ <tr>
+ <td>CreateUser</td>
+ <td>创建者</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateUserID</td>
+ <td>创建人</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateIP</td>
+ <td>创建地址</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateTime</td>
+ <td>创建时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateUser</td>
+ <td>更新者</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateUserID</td>
+ <td>更新人</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateIP</td>
+ <td>更新地址</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateTime</td>
+ <td>更新时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Remark</td>
+ <td>备注</td>
+ <td>String</td>
+ <td>500</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
+<br></br>
+<h3>文档(Document)</h3>
+<table>
+ <thead>
+ <tr>
+ <th>名称</th>
+ <th>显示名</th>
+ <th>类型</th>
+ <th>长度</th>
+ <th>精度</th>
+ <th>主键</th>
+ <th>允许空</th>
+ <th>备注</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Id</td>
+ <td>编号</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td title="自增">AI</td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Code</td>
+ <td>编码</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td title="唯一索引">UQ</td>
+ <td></td>
+ <td>路径唯一标识,默认取Slug</td>
+ </tr>
+
+ <tr>
+ <td>Title</td>
+ <td>标题</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>BookId</td>
+ <td>知识库</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Enable</td>
+ <td>启用</td>
+ <td>Boolean</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UserName</td>
+ <td>用户</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Format</td>
+ <td>格式</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Hits</td>
+ <td>点击量</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Likes</td>
+ <td>点赞数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Comments</td>
+ <td>评论数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Body</td>
+ <td>正文</td>
+ <td>String</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>Markdown格式</td>
+ </tr>
+
+ <tr>
+ <td>BodyHtml</td>
+ <td>HTML正文</td>
+ <td>String</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>ContentUpdateTime</td>
+ <td>内容更新时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>PublishTime</td>
+ <td>发布时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>FirstPublishTime</td>
+ <td>首次发布</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>WordCount</td>
+ <td>单词数</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Cover</td>
+ <td>封面</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateUser</td>
+ <td>创建者</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateUserID</td>
+ <td>创建人</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateIP</td>
+ <td>创建地址</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>CreateTime</td>
+ <td>创建时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateUser</td>
+ <td>更新者</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateUserID</td>
+ <td>更新人</td>
+ <td>Int32</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateIP</td>
+ <td>更新地址</td>
+ <td>String</td>
+ <td>50</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>UpdateTime</td>
+ <td>更新时间</td>
+ <td>DateTime</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Remark</td>
+ <td>备注</td>
+ <td>String</td>
+ <td>500</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
+<br></br>
diff --git "a/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.Biz.cs" "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.Biz.cs"
new file mode 100644
index 0000000..f94c0ad
--- /dev/null
+++ "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.Biz.cs"
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Web.Script.Serialization;
+using System.Xml.Serialization;
+using NewLife;
+using NewLife.Data;
+using NewLife.Log;
+using NewLife.Model;
+using NewLife.Reflection;
+using NewLife.Threading;
+using NewLife.Web;
+using XCode;
+using XCode.Cache;
+using XCode.Configuration;
+using XCode.DataAccessLayer;
+using XCode.Membership;
+using XCode.Shards;
+
+namespace NewLife.YuQueWeb.Entity
+{
+ /// <summary>文档。文档内容</summary>
+ public partial class Document : Entity<Document>
+ {
+ #region 对象操作
+ static Document()
+ {
+ // 累加字段,生成 Update xx Set Count=Count+1234 Where xxx
+ //var df = Meta.Factory.AdditionalFields;
+ //df.Add(nameof(BookId));
+
+ // 过滤器 UserModule、TimeModule、IPModule
+ Meta.Modules.Add<UserModule>();
+ Meta.Modules.Add<TimeModule>();
+ Meta.Modules.Add<IPModule>();
+ }
+
+ /// <summary>验证并修补数据,通过抛出异常的方式提示验证失败。</summary>
+ /// <param name="isNew">是否插入</param>
+ public override void Valid(Boolean isNew)
+ {
+ // 如果没有脏数据,则不需要进行任何处理
+ if (!HasDirty) return;
+
+ // 这里验证参数范围,建议抛出参数异常,指定参数名,前端用户界面可以捕获参数异常并聚焦到对应的参数输入框
+ if (Title.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Title), "标题不能为空!");
+
+ // 建议先调用基类方法,基类方法会做一些统一处理
+ base.Valid(isNew);
+
+ // 在新插入数据或者修改了指定字段时进行修正
+ // 处理当前已登录用户信息,可以由UserModule过滤器代劳
+ /*var user = ManageProvider.User;
+ if (user != null)
+ {
+ if (isNew && !Dirtys[nameof(CreateUserID)]) CreateUserID = user.ID;
+ if (!Dirtys[nameof(UpdateUserID)]) UpdateUserID = user.ID;
+ }*/
+ //if (isNew && !Dirtys[nameof(CreateTime)]) CreateTime = DateTime.Now;
+ //if (!Dirtys[nameof(UpdateTime)]) UpdateTime = DateTime.Now;
+ //if (isNew && !Dirtys[nameof(CreateIP)]) CreateIP = ManageProvider.UserHost;
+ //if (!Dirtys[nameof(UpdateIP)]) UpdateIP = ManageProvider.UserHost;
+
+ // 检查唯一索引
+ // CheckExist(isNew, nameof(Code));
+ }
+
+ ///// <summary>首次连接数据库时初始化数据,仅用于实体类重载,用户不应该调用该方法</summary>
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ //protected override void InitData()
+ //{
+ // // InitData一般用于当数据表没有数据时添加一些默认数据,该实体类的任何第一次数据库操作都会触发该方法,默认异步调用
+ // if (Meta.Session.Count > 0) return;
+
+ // if (XTrace.Debug) XTrace.WriteLine("开始初始化Document[文档]数据……");
+
+ // var entity = new Document();
+ // entity.Code = "abc";
+ // entity.Title = "abc";
+ // entity.BookId = 0;
+ // entity.Enable = true;
+ // entity.UserName = "abc";
+ // entity.Format = "abc";
+ // entity.Hits = 0;
+ // entity.Likes = 0;
+ // entity.Comments = 0;
+ // entity.Body = "abc";
+ // entity.BodyHtml = "abc";
+ // entity.ContentUpdateTime = DateTime.Now;
+ // entity.PublishTime = DateTime.Now;
+ // entity.FirstPublishTime = DateTime.Now;
+ // entity.WordCount = 0;
+ // entity.Cover = "abc";
+ // entity.CreateUser = "abc";
+ // entity.CreateUserID = 0;
+ // entity.CreateIP = "abc";
+ // entity.CreateTime = DateTime.Now;
+ // entity.UpdateUser = "abc";
+ // entity.UpdateUserID = 0;
+ // entity.UpdateIP = "abc";
+ // entity.UpdateTime = DateTime.Now;
+ // entity.Remark = "abc";
+ // entity.Insert();
+
+ // if (XTrace.Debug) XTrace.WriteLine("完成初始化Document[文档]数据!");
+ //}
+
+ ///// <summary>已重载。基类先调用Valid(true)验证数据,然后在事务保护内调用OnInsert</summary>
+ ///// <returns></returns>
+ //public override Int32 Insert()
+ //{
+ // return base.Insert();
+ //}
+
+ ///// <summary>已重载。在事务保护范围内处理业务,位于Valid之后</summary>
+ ///// <returns></returns>
+ //protected override Int32 OnDelete()
+ //{
+ // return base.OnDelete();
+ //}
+ #endregion
+
+ #region 扩展属性
+ /// <summary>知识库</summary>
+ [XmlIgnore, IgnoreDataMember]
+ //[ScriptIgnore]
+ public Book Book => Extends.Get(nameof(Book), k => Book.FindById(BookId));
+
+ /// <summary>知识库</summary>
+ [Map(nameof(BookId), typeof(Book), "Id")]
+ public String BookName => Book?.Name;
+
+ #endregion
+
+ #region 扩展查询
+ /// <summary>根据编号查找</summary>
+ /// <param name="id">编号</param>
+ /// <returns>实体对象</returns>
+ public static Document FindById(Int32 id)
+ {
+ if (id <= 0) return null;
+
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.Find(e => e.Id == id);
+
+ // 单对象缓存
+ return Meta.SingleCache[id];
+
+ //return Find(_.Id == id);
+ }
+
+ /// <summary>根据编码查找</summary>
+ /// <param name="code">编码</param>
+ /// <returns>实体对象</returns>
+ public static Document FindByCode(String code)
+ {
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.Find(e => e.Code.EqualIgnoreCase(code));
+
+ return Find(_.Code == code);
+ }
+
+ /// <summary>根据知识库查找</summary>
+ /// <param name="bookId">知识库</param>
+ /// <returns>实体列表</returns>
+ public static IList<Document> FindAllByBookId(Int32 bookId)
+ {
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.BookId == bookId);
+
+ return FindAll(_.BookId == bookId);
+ }
+
+ /// <summary>根据标题查找</summary>
+ /// <param name="title">标题</param>
+ /// <returns>实体列表</returns>
+ public static IList<Document> FindAllByTitle(String title)
+ {
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.Title.EqualIgnoreCase(title));
+
+ return FindAll(_.Title == title);
+ }
+ #endregion
+
+ #region 高级查询
+ /// <summary>高级查询</summary>
+ /// <param name="code">编码。路径唯一标识,默认取Slug</param>
+ /// <param name="title">标题</param>
+ /// <param name="bookId">知识库</param>
+ /// <param name="start">更新时间开始</param>
+ /// <param name="end">更新时间结束</param>
+ /// <param name="key">关键字</param>
+ /// <param name="page">分页参数信息。可携带统计和数据权限扩展查询等信息</param>
+ /// <returns>实体列表</returns>
+ public static IList<Document> Search(String code, String title, Int32 bookId, DateTime start, DateTime end, String key, PageParameter page)
+ {
+ var exp = new WhereExpression();
+
+ if (!code.IsNullOrEmpty()) exp &= _.Code == code;
+ if (!title.IsNullOrEmpty()) exp &= _.Title == title;
+ if (bookId >= 0) exp &= _.BookId == bookId;
+ exp &= _.UpdateTime.Between(start, end);
+ if (!key.IsNullOrEmpty()) exp &= _.Code.Contains(key) | _.Title.Contains(key) | _.UserName.Contains(key) | _.Format.Contains(key) | _.Body.Contains(key) | _.BodyHtml.Contains(key) | _.Cover.Contains(key) | _.CreateUser.Contains(key) | _.CreateIP.Contains(key) | _.UpdateUser.Contains(key) | _.UpdateIP.Contains(key) | _.Remark.Contains(key);
+
+ return FindAll(exp, page);
+ }
+
+ // Select Count(Id) as Id,Category From Document Where CreateTime>'2020-01-24 00:00:00' Group By Category Order By Id Desc limit 20
+ //static readonly FieldCache<Document> _CategoryCache = new FieldCache<Document>(nameof(Category))
+ //{
+ //Where = _.CreateTime > DateTime.Today.AddDays(-30) & Expression.Empty
+ //};
+
+ ///// <summary>获取类别列表,字段缓存10分钟,分组统计数据最多的前20种,用于魔方前台下拉选择</summary>
+ ///// <returns></returns>
+ //public static IDictionary<String, String> GetCategoryList() => _CategoryCache.FindAllName();
+ #endregion
+
+ #region 业务操作
+ #endregion
+ }
+}
\ No newline at end of file
diff --git "a/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs" "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs"
new file mode 100644
index 0000000..b68f759
--- /dev/null
+++ "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs"
@@ -0,0 +1,476 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.Serialization;
+using System.Web.Script.Serialization;
+using System.Xml.Serialization;
+using XCode;
+using XCode.Configuration;
+using XCode.DataAccessLayer;
+
+namespace NewLife.YuQueWeb.Entity
+{
+ /// <summary>文档。文档内容</summary>
+ [Serializable]
+ [DataObject]
+ [Description("文档。文档内容")]
+ [BindIndex("IU_Document_Code", true, "Code")]
+ [BindIndex("IX_Document_BookId", false, "BookId")]
+ [BindIndex("IX_Document_Title", false, "Title")]
+ [BindTable("Document", Description = "文档。文档内容", ConnName = "YuQue", DbType = DatabaseType.None)]
+ public partial class Document
+ {
+ #region 属性
+ private Int32 _Id;
+ /// <summary>编号</summary>
+ [DisplayName("编号")]
+ [Description("编号")]
+ [DataObjectField(true, true, false, 0)]
+ [BindColumn("Id", "编号", "")]
+ public Int32 Id { get => _Id; set { if (OnPropertyChanging("Id", value)) { _Id = value; OnPropertyChanged("Id"); } } }
+
+ private String _Code;
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ [DisplayName("编码")]
+ [Description("编码。路径唯一标识,默认取Slug")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Code", "编码。路径唯一标识,默认取Slug", "")]
+ public String Code { get => _Code; set { if (OnPropertyChanging("Code", value)) { _Code = value; OnPropertyChanged("Code"); } } }
+
+ private String _Title;
+ /// <summary>标题</summary>
+ [DisplayName("标题")]
+ [Description("标题")]
+ [DataObjectField(false, false, false, 50)]
+ [BindColumn("Title", "标题", "", Master = true)]
+ public String Title { get => _Title; set { if (OnPropertyChanging("Title", value)) { _Title = value; OnPropertyChanged("Title"); } } }
+
+ private Int32 _BookId;
+ /// <summary>知识库</summary>
+ [DisplayName("知识库")]
+ [Description("知识库")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("BookId", "知识库", "")]
+ public Int32 BookId { get => _BookId; set { if (OnPropertyChanging("BookId", value)) { _BookId = value; OnPropertyChanged("BookId"); } } }
+
+ private Boolean _Enable;
+ /// <summary>启用</summary>
+ [DisplayName("启用")]
+ [Description("启用")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Enable", "启用", "")]
+ public Boolean Enable { get => _Enable; set { if (OnPropertyChanging("Enable", value)) { _Enable = value; OnPropertyChanged("Enable"); } } }
+
+ private String _UserName;
+ /// <summary>用户</summary>
+ [DisplayName("用户")]
+ [Description("用户")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UserName", "用户", "")]
+ public String UserName { get => _UserName; set { if (OnPropertyChanging("UserName", value)) { _UserName = value; OnPropertyChanged("UserName"); } } }
+
+ private String _Format;
+ /// <summary>格式</summary>
+ [DisplayName("格式")]
+ [Description("格式")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Format", "格式", "")]
+ public String Format { get => _Format; set { if (OnPropertyChanging("Format", value)) { _Format = value; OnPropertyChanged("Format"); } } }
+
+ private Int32 _Hits;
+ /// <summary>点击量</summary>
+ [DisplayName("点击量")]
+ [Description("点击量")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Hits", "点击量", "")]
+ public Int32 Hits { get => _Hits; set { if (OnPropertyChanging("Hits", value)) { _Hits = value; OnPropertyChanged("Hits"); } } }
+
+ private Int32 _Likes;
+ /// <summary>点赞数</summary>
+ [DisplayName("点赞数")]
+ [Description("点赞数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Likes", "点赞数", "")]
+ public Int32 Likes { get => _Likes; set { if (OnPropertyChanging("Likes", value)) { _Likes = value; OnPropertyChanged("Likes"); } } }
+
+ private Int32 _Comments;
+ /// <summary>评论数</summary>
+ [DisplayName("评论数")]
+ [Description("评论数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Comments", "评论数", "")]
+ public Int32 Comments { get => _Comments; set { if (OnPropertyChanging("Comments", value)) { _Comments = value; OnPropertyChanged("Comments"); } } }
+
+ private String _Body;
+ /// <summary>正文。Markdown格式</summary>
+ [DisplayName("正文")]
+ [Description("正文。Markdown格式")]
+ [DataObjectField(false, false, true, -1)]
+ [BindColumn("Body", "正文。Markdown格式", "")]
+ public String Body { get => _Body; set { if (OnPropertyChanging("Body", value)) { _Body = value; OnPropertyChanged("Body"); } } }
+
+ private String _BodyHtml;
+ /// <summary>HTML正文</summary>
+ [DisplayName("HTML正文")]
+ [Description("HTML正文")]
+ [DataObjectField(false, false, true, -1)]
+ [BindColumn("BodyHtml", "HTML正文", "")]
+ public String BodyHtml { get => _BodyHtml; set { if (OnPropertyChanging("BodyHtml", value)) { _BodyHtml = value; OnPropertyChanged("BodyHtml"); } } }
+
+ private DateTime _ContentUpdateTime;
+ /// <summary>内容更新时间</summary>
+ [DisplayName("内容更新时间")]
+ [Description("内容更新时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("ContentUpdateTime", "内容更新时间", "")]
+ public DateTime ContentUpdateTime { get => _ContentUpdateTime; set { if (OnPropertyChanging("ContentUpdateTime", value)) { _ContentUpdateTime = value; OnPropertyChanged("ContentUpdateTime"); } } }
+
+ private DateTime _PublishTime;
+ /// <summary>发布时间</summary>
+ [DisplayName("发布时间")]
+ [Description("发布时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("PublishTime", "发布时间", "")]
+ public DateTime PublishTime { get => _PublishTime; set { if (OnPropertyChanging("PublishTime", value)) { _PublishTime = value; OnPropertyChanged("PublishTime"); } } }
+
+ private DateTime _FirstPublishTime;
+ /// <summary>首次发布</summary>
+ [DisplayName("首次发布")]
+ [Description("首次发布")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("FirstPublishTime", "首次发布", "")]
+ public DateTime FirstPublishTime { get => _FirstPublishTime; set { if (OnPropertyChanging("FirstPublishTime", value)) { _FirstPublishTime = value; OnPropertyChanged("FirstPublishTime"); } } }
+
+ private Int32 _WordCount;
+ /// <summary>单词数</summary>
+ [DisplayName("单词数")]
+ [Description("单词数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("WordCount", "单词数", "")]
+ public Int32 WordCount { get => _WordCount; set { if (OnPropertyChanging("WordCount", value)) { _WordCount = value; OnPropertyChanged("WordCount"); } } }
+
+ private String _Cover;
+ /// <summary>封面</summary>
+ [DisplayName("封面")]
+ [Description("封面")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Cover", "封面", "")]
+ public String Cover { get => _Cover; set { if (OnPropertyChanging("Cover", value)) { _Cover = value; OnPropertyChanged("Cover"); } } }
+
+ private String _CreateUser;
+ /// <summary>创建者</summary>
+ [DisplayName("创建者")]
+ [Description("创建者")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("CreateUser", "创建者", "")]
+ public String CreateUser { get => _CreateUser; set { if (OnPropertyChanging("CreateUser", value)) { _CreateUser = value; OnPropertyChanged("CreateUser"); } } }
+
+ private Int32 _CreateUserID;
+ /// <summary>创建人</summary>
+ [DisplayName("创建人")]
+ [Description("创建人")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("CreateUserID", "创建人", "")]
+ public Int32 CreateUserID { get => _CreateUserID; set { if (OnPropertyChanging("CreateUserID", value)) { _CreateUserID = value; OnPropertyChanged("CreateUserID"); } } }
+
+ private String _CreateIP;
+ /// <summary>创建地址</summary>
+ [DisplayName("创建地址")]
+ [Description("创建地址")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("CreateIP", "创建地址", "")]
+ public String CreateIP { get => _CreateIP; set { if (OnPropertyChanging("CreateIP", value)) { _CreateIP = value; OnPropertyChanged("CreateIP"); } } }
+
+ private DateTime _CreateTime;
+ /// <summary>创建时间</summary>
+ [DisplayName("创建时间")]
+ [Description("创建时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("CreateTime", "创建时间", "")]
+ public DateTime CreateTime { get => _CreateTime; set { if (OnPropertyChanging("CreateTime", value)) { _CreateTime = value; OnPropertyChanged("CreateTime"); } } }
+
+ private String _UpdateUser;
+ /// <summary>更新者</summary>
+ [DisplayName("更新者")]
+ [Description("更新者")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UpdateUser", "更新者", "")]
+ public String UpdateUser { get => _UpdateUser; set { if (OnPropertyChanging("UpdateUser", value)) { _UpdateUser = value; OnPropertyChanged("UpdateUser"); } } }
+
+ private Int32 _UpdateUserID;
+ /// <summary>更新人</summary>
+ [DisplayName("更新人")]
+ [Description("更新人")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("UpdateUserID", "更新人", "")]
+ public Int32 UpdateUserID { get => _UpdateUserID; set { if (OnPropertyChanging("UpdateUserID", value)) { _UpdateUserID = value; OnPropertyChanged("UpdateUserID"); } } }
+
+ private String _UpdateIP;
+ /// <summary>更新地址</summary>
+ [DisplayName("更新地址")]
+ [Description("更新地址")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UpdateIP", "更新地址", "")]
+ public String UpdateIP { get => _UpdateIP; set { if (OnPropertyChanging("UpdateIP", value)) { _UpdateIP = value; OnPropertyChanged("UpdateIP"); } } }
+
+ private DateTime _UpdateTime;
+ /// <summary>更新时间</summary>
+ [DisplayName("更新时间")]
+ [Description("更新时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("UpdateTime", "更新时间", "")]
+ public DateTime UpdateTime { get => _UpdateTime; set { if (OnPropertyChanging("UpdateTime", value)) { _UpdateTime = value; OnPropertyChanged("UpdateTime"); } } }
+
+ private String _Remark;
+ /// <summary>备注</summary>
+ [DisplayName("备注")]
+ [Description("备注")]
+ [DataObjectField(false, false, true, 500)]
+ [BindColumn("Remark", "备注", "")]
+ public String Remark { get => _Remark; set { if (OnPropertyChanging("Remark", value)) { _Remark = value; OnPropertyChanged("Remark"); } } }
+ #endregion
+
+ #region 获取/设置 字段值
+ /// <summary>获取/设置 字段值</summary>
+ /// <param name="name">字段名</param>
+ /// <returns></returns>
+ public override Object this[String name]
+ {
+ get
+ {
+ switch (name)
+ {
+ case "Id": return _Id;
+ case "Code": return _Code;
+ case "Title": return _Title;
+ case "BookId": return _BookId;
+ case "Enable": return _Enable;
+ case "UserName": return _UserName;
+ case "Format": return _Format;
+ case "Hits": return _Hits;
+ case "Likes": return _Likes;
+ case "Comments": return _Comments;
+ case "Body": return _Body;
+ case "BodyHtml": return _BodyHtml;
+ case "ContentUpdateTime": return _ContentUpdateTime;
+ case "PublishTime": return _PublishTime;
+ case "FirstPublishTime": return _FirstPublishTime;
+ case "WordCount": return _WordCount;
+ case "Cover": return _Cover;
+ case "CreateUser": return _CreateUser;
+ case "CreateUserID": return _CreateUserID;
+ case "CreateIP": return _CreateIP;
+ case "CreateTime": return _CreateTime;
+ case "UpdateUser": return _UpdateUser;
+ case "UpdateUserID": return _UpdateUserID;
+ case "UpdateIP": return _UpdateIP;
+ case "UpdateTime": return _UpdateTime;
+ case "Remark": return _Remark;
+ default: return base[name];
+ }
+ }
+ set
+ {
+ switch (name)
+ {
+ case "Id": _Id = value.ToInt(); break;
+ case "Code": _Code = Convert.ToString(value); break;
+ case "Title": _Title = Convert.ToString(value); break;
+ case "BookId": _BookId = value.ToInt(); break;
+ case "Enable": _Enable = value.ToBoolean(); break;
+ case "UserName": _UserName = Convert.ToString(value); break;
+ case "Format": _Format = Convert.ToString(value); break;
+ case "Hits": _Hits = value.ToInt(); break;
+ case "Likes": _Likes = value.ToInt(); break;
+ case "Comments": _Comments = value.ToInt(); break;
+ case "Body": _Body = Convert.ToString(value); break;
+ case "BodyHtml": _BodyHtml = Convert.ToString(value); break;
+ case "ContentUpdateTime": _ContentUpdateTime = value.ToDateTime(); break;
+ case "PublishTime": _PublishTime = value.ToDateTime(); break;
+ case "FirstPublishTime": _FirstPublishTime = value.ToDateTime(); break;
+ case "WordCount": _WordCount = value.ToInt(); break;
+ case "Cover": _Cover = Convert.ToString(value); break;
+ case "CreateUser": _CreateUser = Convert.ToString(value); break;
+ case "CreateUserID": _CreateUserID = value.ToInt(); break;
+ case "CreateIP": _CreateIP = Convert.ToString(value); break;
+ case "CreateTime": _CreateTime = value.ToDateTime(); break;
+ case "UpdateUser": _UpdateUser = Convert.ToString(value); break;
+ case "UpdateUserID": _UpdateUserID = value.ToInt(); break;
+ case "UpdateIP": _UpdateIP = Convert.ToString(value); break;
+ case "UpdateTime": _UpdateTime = value.ToDateTime(); break;
+ case "Remark": _Remark = Convert.ToString(value); break;
+ default: base[name] = value; break;
+ }
+ }
+ }
+ #endregion
+
+ #region 字段名
+ /// <summary>取得文档字段信息的快捷方式</summary>
+ public partial class _
+ {
+ /// <summary>编号</summary>
+ public static readonly Field Id = FindByName("Id");
+
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ public static readonly Field Code = FindByName("Code");
+
+ /// <summary>标题</summary>
+ public static readonly Field Title = FindByName("Title");
+
+ /// <summary>知识库</summary>
+ public static readonly Field BookId = FindByName("BookId");
+
+ /// <summary>启用</summary>
+ public static readonly Field Enable = FindByName("Enable");
+
+ /// <summary>用户</summary>
+ public static readonly Field UserName = FindByName("UserName");
+
+ /// <summary>格式</summary>
+ public static readonly Field Format = FindByName("Format");
+
+ /// <summary>点击量</summary>
+ public static readonly Field Hits = FindByName("Hits");
+
+ /// <summary>点赞数</summary>
+ public static readonly Field Likes = FindByName("Likes");
+
+ /// <summary>评论数</summary>
+ public static readonly Field Comments = FindByName("Comments");
+
+ /// <summary>正文。Markdown格式</summary>
+ public static readonly Field Body = FindByName("Body");
+
+ /// <summary>HTML正文</summary>
+ public static readonly Field BodyHtml = FindByName("BodyHtml");
+
+ /// <summary>内容更新时间</summary>
+ public static readonly Field ContentUpdateTime = FindByName("ContentUpdateTime");
+
+ /// <summary>发布时间</summary>
+ public static readonly Field PublishTime = FindByName("PublishTime");
+
+ /// <summary>首次发布</summary>
+ public static readonly Field FirstPublishTime = FindByName("FirstPublishTime");
+
+ /// <summary>单词数</summary>
+ public static readonly Field WordCount = FindByName("WordCount");
+
+ /// <summary>封面</summary>
+ public static readonly Field Cover = FindByName("Cover");
+
+ /// <summary>创建者</summary>
+ public static readonly Field CreateUser = FindByName("CreateUser");
+
+ /// <summary>创建人</summary>
+ public static readonly Field CreateUserID = FindByName("CreateUserID");
+
+ /// <summary>创建地址</summary>
+ public static readonly Field CreateIP = FindByName("CreateIP");
+
+ /// <summary>创建时间</summary>
+ public static readonly Field CreateTime = FindByName("CreateTime");
+
+ /// <summary>更新者</summary>
+ public static readonly Field UpdateUser = FindByName("UpdateUser");
+
+ /// <summary>更新人</summary>
+ public static readonly Field UpdateUserID = FindByName("UpdateUserID");
+
+ /// <summary>更新地址</summary>
+ public static readonly Field UpdateIP = FindByName("UpdateIP");
+
+ /// <summary>更新时间</summary>
+ public static readonly Field UpdateTime = FindByName("UpdateTime");
+
+ /// <summary>备注</summary>
+ public static readonly Field Remark = FindByName("Remark");
+
+ static Field FindByName(String name) => Meta.Table.FindByName(name);
+ }
+
+ /// <summary>取得文档字段名称的快捷方式</summary>
+ public partial class __
+ {
+ /// <summary>编号</summary>
+ public const String Id = "Id";
+
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ public const String Code = "Code";
+
+ /// <summary>标题</summary>
+ public const String Title = "Title";
+
+ /// <summary>知识库</summary>
+ public const String BookId = "BookId";
+
+ /// <summary>启用</summary>
+ public const String Enable = "Enable";
+
+ /// <summary>用户</summary>
+ public const String UserName = "UserName";
+
+ /// <summary>格式</summary>
+ public const String Format = "Format";
+
+ /// <summary>点击量</summary>
+ public const String Hits = "Hits";
+
+ /// <summary>点赞数</summary>
+ public const String Likes = "Likes";
+
+ /// <summary>评论数</summary>
+ public const String Comments = "Comments";
+
+ /// <summary>正文。Markdown格式</summary>
+ public const String Body = "Body";
+
+ /// <summary>HTML正文</summary>
+ public const String BodyHtml = "BodyHtml";
+
+ /// <summary>内容更新时间</summary>
+ public const String ContentUpdateTime = "ContentUpdateTime";
+
+ /// <summary>发布时间</summary>
+ public const String PublishTime = "PublishTime";
+
+ /// <summary>首次发布</summary>
+ public const String FirstPublishTime = "FirstPublishTime";
+
+ /// <summary>单词数</summary>
+ public const String WordCount = "WordCount";
+
+ /// <summary>封面</summary>
+ public const String Cover = "Cover";
+
+ /// <summary>创建者</summary>
+ public const String CreateUser = "CreateUser";
+
+ /// <summary>创建人</summary>
+ public const String CreateUserID = "CreateUserID";
+
+ /// <summary>创建地址</summary>
+ public const String CreateIP = "CreateIP";
+
+ /// <summary>创建时间</summary>
+ public const String CreateTime = "CreateTime";
+
+ /// <summary>更新者</summary>
+ public const String UpdateUser = "UpdateUser";
+
+ /// <summary>更新人</summary>
+ public const String UpdateUserID = "UpdateUserID";
+
+ /// <summary>更新地址</summary>
+ public const String UpdateIP = "UpdateIP";
+
+ /// <summary>更新时间</summary>
+ public const String UpdateTime = "UpdateTime";
+
+ /// <summary>备注</summary>
+ public const String Remark = "Remark";
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git "a/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.Biz.cs" "b/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.Biz.cs"
new file mode 100644
index 0000000..9c2d88e
--- /dev/null
+++ "b/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.Biz.cs"
@@ -0,0 +1,204 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Web.Script.Serialization;
+using System.Xml.Serialization;
+using NewLife;
+using NewLife.Data;
+using NewLife.Log;
+using NewLife.Model;
+using NewLife.Reflection;
+using NewLife.Threading;
+using NewLife.Web;
+using XCode;
+using XCode.Cache;
+using XCode.Configuration;
+using XCode.DataAccessLayer;
+using XCode.Membership;
+using XCode.Shards;
+
+namespace NewLife.YuQueWeb.Entity
+{
+ /// <summary>知识库。管理知识库</summary>
+ public partial class Book : Entity<Book>
+ {
+ #region 对象操作
+ static Book()
+ {
+ // 累加字段,生成 Update xx Set Count=Count+1234 Where xxx
+ //var df = Meta.Factory.AdditionalFields;
+ //df.Add(nameof(Docs));
+
+ // 过滤器 UserModule、TimeModule、IPModule
+ Meta.Modules.Add<UserModule>();
+ Meta.Modules.Add<TimeModule>();
+ Meta.Modules.Add<IPModule>();
+ }
+
+ /// <summary>验证并修补数据,通过抛出异常的方式提示验证失败。</summary>
+ /// <param name="isNew">是否插入</param>
+ public override void Valid(Boolean isNew)
+ {
+ // 如果没有脏数据,则不需要进行任何处理
+ if (!HasDirty) return;
+
+ // 这里验证参数范围,建议抛出参数异常,指定参数名,前端用户界面可以捕获参数异常并聚焦到对应的参数输入框
+ if (Name.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Name), "名称不能为空!");
+
+ // 建议先调用基类方法,基类方法会做一些统一处理
+ base.Valid(isNew);
+
+ // 在新插入数据或者修改了指定字段时进行修正
+ // 处理当前已登录用户信息,可以由UserModule过滤器代劳
+ /*var user = ManageProvider.User;
+ if (user != null)
+ {
+ if (isNew && !Dirtys[nameof(CreateUserID)]) CreateUserID = user.ID;
+ if (!Dirtys[nameof(UpdateUserID)]) UpdateUserID = user.ID;
+ }*/
+ //if (isNew && !Dirtys[nameof(CreateTime)]) CreateTime = DateTime.Now;
+ //if (!Dirtys[nameof(UpdateTime)]) UpdateTime = DateTime.Now;
+ //if (isNew && !Dirtys[nameof(CreateIP)]) CreateIP = ManageProvider.UserHost;
+ //if (!Dirtys[nameof(UpdateIP)]) UpdateIP = ManageProvider.UserHost;
+
+ // 检查唯一索引
+ // CheckExist(isNew, nameof(Code));
+ }
+
+ ///// <summary>首次连接数据库时初始化数据,仅用于实体类重载,用户不应该调用该方法</summary>
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ //protected override void InitData()
+ //{
+ // // InitData一般用于当数据表没有数据时添加一些默认数据,该实体类的任何第一次数据库操作都会触发该方法,默认异步调用
+ // if (Meta.Session.Count > 0) return;
+
+ // if (XTrace.Debug) XTrace.WriteLine("开始初始化Book[知识库]数据……");
+
+ // var entity = new Book();
+ // entity.Code = "abc";
+ // entity.Name = "abc";
+ // entity.Type = "abc";
+ // entity.Enable = true;
+ // entity.UserName = "abc";
+ // entity.Docs = 0;
+ // entity.Likes = 0;
+ // entity.Watches = 0;
+ // entity.Sync = true;
+ // entity.Slug = "abc";
+ // entity.Namespace = "abc";
+ // entity.SyncTime = DateTime.Now;
+ // entity.CreateUser = "abc";
+ // entity.CreateUserID = 0;
+ // entity.CreateIP = "abc";
+ // entity.CreateTime = DateTime.Now;
+ // entity.UpdateUser = "abc";
+ // entity.UpdateUserID = 0;
+ // entity.UpdateIP = "abc";
+ // entity.UpdateTime = DateTime.Now;
+ // entity.Remark = "abc";
+ // entity.Insert();
+
+ // if (XTrace.Debug) XTrace.WriteLine("完成初始化Book[知识库]数据!");
+ //}
+
+ ///// <summary>已重载。基类先调用Valid(true)验证数据,然后在事务保护内调用OnInsert</summary>
+ ///// <returns></returns>
+ //public override Int32 Insert()
+ //{
+ // return base.Insert();
+ //}
+
+ ///// <summary>已重载。在事务保护范围内处理业务,位于Valid之后</summary>
+ ///// <returns></returns>
+ //protected override Int32 OnDelete()
+ //{
+ // return base.OnDelete();
+ //}
+ #endregion
+
+ #region 扩展属性
+ #endregion
+
+ #region 扩展查询
+ /// <summary>根据编号查找</summary>
+ /// <param name="id">编号</param>
+ /// <returns>实体对象</returns>
+ public static Book FindById(Int32 id)
+ {
+ if (id <= 0) return null;
+
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.Find(e => e.Id == id);
+
+ // 单对象缓存
+ return Meta.SingleCache[id];
+
+ //return Find(_.Id == id);
+ }
+
+ /// <summary>根据编码查找</summary>
+ /// <param name="code">编码</param>
+ /// <returns>实体对象</returns>
+ public static Book FindByCode(String code)
+ {
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.Find(e => e.Code.EqualIgnoreCase(code));
+
+ return Find(_.Code == code);
+ }
+
+ /// <summary>根据名称查找</summary>
+ /// <param name="name">名称</param>
+ /// <returns>实体列表</returns>
+ public static IList<Book> FindAllByName(String name)
+ {
+ // 实体缓存
+ if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.Name.EqualIgnoreCase(name));
+
+ return FindAll(_.Name == name);
+ }
+ #endregion
+
+ #region 高级查询
+ /// <summary>高级查询</summary>
+ /// <param name="code">编码。路径唯一标识,默认取Slug</param>
+ /// <param name="name">名称</param>
+ /// <param name="start">更新时间开始</param>
+ /// <param name="end">更新时间结束</param>
+ /// <param name="key">关键字</param>
+ /// <param name="page">分页参数信息。可携带统计和数据权限扩展查询等信息</param>
+ /// <returns>实体列表</returns>
+ public static IList<Book> Search(String code, String name, DateTime start, DateTime end, String key, PageParameter page)
+ {
+ var exp = new WhereExpression();
+
+ if (!code.IsNullOrEmpty()) exp &= _.Code == code;
+ if (!name.IsNullOrEmpty()) exp &= _.Name == name;
+ exp &= _.UpdateTime.Between(start, end);
+ if (!key.IsNullOrEmpty()) exp &= _.Code.Contains(key) | _.Name.Contains(key) | _.Type.Contains(key) | _.UserName.Contains(key) | _.Slug.Contains(key) | _.Namespace.Contains(key) | _.CreateUser.Contains(key) | _.CreateIP.Contains(key) | _.UpdateUser.Contains(key) | _.UpdateIP.Contains(key) | _.Remark.Contains(key);
+
+ return FindAll(exp, page);
+ }
+
+ // Select Count(Id) as Id,Category From Book Where CreateTime>'2020-01-24 00:00:00' Group By Category Order By Id Desc limit 20
+ //static readonly FieldCache<Book> _CategoryCache = new FieldCache<Book>(nameof(Category))
+ //{
+ //Where = _.CreateTime > DateTime.Today.AddDays(-30) & Expression.Empty
+ //};
+
+ ///// <summary>获取类别列表,字段缓存10分钟,分组统计数据最多的前20种,用于魔方前台下拉选择</summary>
+ ///// <returns></returns>
+ //public static IDictionary<String, String> GetCategoryList() => _CategoryCache.FindAllName();
+ #endregion
+
+ #region 业务操作
+ #endregion
+ }
+}
\ No newline at end of file
diff --git "a/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.cs" "b/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.cs"
new file mode 100644
index 0000000..e0c5f95
--- /dev/null
+++ "b/NewLife.YuqueWeb/Entity/\347\237\245\350\257\206\345\272\223.cs"
@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.Serialization;
+using System.Web.Script.Serialization;
+using System.Xml.Serialization;
+using XCode;
+using XCode.Configuration;
+using XCode.DataAccessLayer;
+
+namespace NewLife.YuQueWeb.Entity
+{
+ /// <summary>知识库。管理知识库</summary>
+ [Serializable]
+ [DataObject]
+ [Description("知识库。管理知识库")]
+ [BindIndex("IU_Book_Code", true, "Code")]
+ [BindIndex("IX_Book_Name", false, "Name")]
+ [BindTable("Book", Description = "知识库。管理知识库", ConnName = "YuQue", DbType = DatabaseType.None)]
+ public partial class Book
+ {
+ #region 属性
+ private Int32 _Id;
+ /// <summary>编号</summary>
+ [DisplayName("编号")]
+ [Description("编号")]
+ [DataObjectField(true, true, false, 0)]
+ [BindColumn("Id", "编号", "")]
+ public Int32 Id { get => _Id; set { if (OnPropertyChanging("Id", value)) { _Id = value; OnPropertyChanged("Id"); } } }
+
+ private String _Code;
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ [DisplayName("编码")]
+ [Description("编码。路径唯一标识,默认取Slug")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Code", "编码。路径唯一标识,默认取Slug", "")]
+ public String Code { get => _Code; set { if (OnPropertyChanging("Code", value)) { _Code = value; OnPropertyChanged("Code"); } } }
+
+ private String _Name;
+ /// <summary>名称</summary>
+ [DisplayName("名称")]
+ [Description("名称")]
+ [DataObjectField(false, false, false, 50)]
+ [BindColumn("Name", "名称", "", Master = true)]
+ public String Name { get => _Name; set { if (OnPropertyChanging("Name", value)) { _Name = value; OnPropertyChanged("Name"); } } }
+
+ private String _Type;
+ /// <summary>类型</summary>
+ [DisplayName("类型")]
+ [Description("类型")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Type", "类型", "")]
+ public String Type { get => _Type; set { if (OnPropertyChanging("Type", value)) { _Type = value; OnPropertyChanged("Type"); } } }
+
+ private Boolean _Enable;
+ /// <summary>启用</summary>
+ [DisplayName("启用")]
+ [Description("启用")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Enable", "启用", "")]
+ public Boolean Enable { get => _Enable; set { if (OnPropertyChanging("Enable", value)) { _Enable = value; OnPropertyChanged("Enable"); } } }
+
+ private String _UserName;
+ /// <summary>用户</summary>
+ [DisplayName("用户")]
+ [Description("用户")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UserName", "用户", "")]
+ public String UserName { get => _UserName; set { if (OnPropertyChanging("UserName", value)) { _UserName = value; OnPropertyChanged("UserName"); } } }
+
+ private Int32 _Docs;
+ /// <summary>文章数</summary>
+ [DisplayName("文章数")]
+ [Description("文章数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Docs", "文章数", "")]
+ public Int32 Docs { get => _Docs; set { if (OnPropertyChanging("Docs", value)) { _Docs = value; OnPropertyChanged("Docs"); } } }
+
+ private Int32 _Likes;
+ /// <summary>点赞数</summary>
+ [DisplayName("点赞数")]
+ [Description("点赞数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Likes", "点赞数", "")]
+ public Int32 Likes { get => _Likes; set { if (OnPropertyChanging("Likes", value)) { _Likes = value; OnPropertyChanged("Likes"); } } }
+
+ private Int32 _Watches;
+ /// <summary>订阅数</summary>
+ [DisplayName("订阅数")]
+ [Description("订阅数")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Watches", "订阅数", "")]
+ public Int32 Watches { get => _Watches; set { if (OnPropertyChanging("Watches", value)) { _Watches = value; OnPropertyChanged("Watches"); } } }
+
+ private Boolean _Sync;
+ /// <summary>同步。是否自动同步远程内容</summary>
+ [DisplayName("同步")]
+ [Description("同步。是否自动同步远程内容")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("Sync", "同步。是否自动同步远程内容", "")]
+ public Boolean Sync { get => _Sync; set { if (OnPropertyChanging("Sync", value)) { _Sync = value; OnPropertyChanged("Sync"); } } }
+
+ private String _Slug;
+ /// <summary>路径</summary>
+ [DisplayName("路径")]
+ [Description("路径")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Slug", "路径", "")]
+ public String Slug { get => _Slug; set { if (OnPropertyChanging("Slug", value)) { _Slug = value; OnPropertyChanged("Slug"); } } }
+
+ private String _Namespace;
+ /// <summary>全路径</summary>
+ [DisplayName("全路径")]
+ [Description("全路径")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("Namespace", "全路径", "")]
+ public String Namespace { get => _Namespace; set { if (OnPropertyChanging("Namespace", value)) { _Namespace = value; OnPropertyChanged("Namespace"); } } }
+
+ private DateTime _SyncTime;
+ /// <summary>同步时间。最后一次同步数据的时间</summary>
+ [DisplayName("同步时间")]
+ [Description("同步时间。最后一次同步数据的时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("SyncTime", "同步时间。最后一次同步数据的时间", "")]
+ public DateTime SyncTime { get => _SyncTime; set { if (OnPropertyChanging("SyncTime", value)) { _SyncTime = value; OnPropertyChanged("SyncTime"); } } }
+
+ private String _CreateUser;
+ /// <summary>创建者</summary>
+ [DisplayName("创建者")]
+ [Description("创建者")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("CreateUser", "创建者", "")]
+ public String CreateUser { get => _CreateUser; set { if (OnPropertyChanging("CreateUser", value)) { _CreateUser = value; OnPropertyChanged("CreateUser"); } } }
+
+ private Int32 _CreateUserID;
+ /// <summary>创建人</summary>
+ [DisplayName("创建人")]
+ [Description("创建人")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("CreateUserID", "创建人", "")]
+ public Int32 CreateUserID { get => _CreateUserID; set { if (OnPropertyChanging("CreateUserID", value)) { _CreateUserID = value; OnPropertyChanged("CreateUserID"); } } }
+
+ private String _CreateIP;
+ /// <summary>创建地址</summary>
+ [DisplayName("创建地址")]
+ [Description("创建地址")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("CreateIP", "创建地址", "")]
+ public String CreateIP { get => _CreateIP; set { if (OnPropertyChanging("CreateIP", value)) { _CreateIP = value; OnPropertyChanged("CreateIP"); } } }
+
+ private DateTime _CreateTime;
+ /// <summary>创建时间</summary>
+ [DisplayName("创建时间")]
+ [Description("创建时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("CreateTime", "创建时间", "")]
+ public DateTime CreateTime { get => _CreateTime; set { if (OnPropertyChanging("CreateTime", value)) { _CreateTime = value; OnPropertyChanged("CreateTime"); } } }
+
+ private String _UpdateUser;
+ /// <summary>更新者</summary>
+ [DisplayName("更新者")]
+ [Description("更新者")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UpdateUser", "更新者", "")]
+ public String UpdateUser { get => _UpdateUser; set { if (OnPropertyChanging("UpdateUser", value)) { _UpdateUser = value; OnPropertyChanged("UpdateUser"); } } }
+
+ private Int32 _UpdateUserID;
+ /// <summary>更新人</summary>
+ [DisplayName("更新人")]
+ [Description("更新人")]
+ [DataObjectField(false, false, false, 0)]
+ [BindColumn("UpdateUserID", "更新人", "")]
+ public Int32 UpdateUserID { get => _UpdateUserID; set { if (OnPropertyChanging("UpdateUserID", value)) { _UpdateUserID = value; OnPropertyChanged("UpdateUserID"); } } }
+
+ private String _UpdateIP;
+ /// <summary>更新地址</summary>
+ [DisplayName("更新地址")]
+ [Description("更新地址")]
+ [DataObjectField(false, false, true, 50)]
+ [BindColumn("UpdateIP", "更新地址", "")]
+ public String UpdateIP { get => _UpdateIP; set { if (OnPropertyChanging("UpdateIP", value)) { _UpdateIP = value; OnPropertyChanged("UpdateIP"); } } }
+
+ private DateTime _UpdateTime;
+ /// <summary>更新时间</summary>
+ [DisplayName("更新时间")]
+ [Description("更新时间")]
+ [DataObjectField(false, false, true, 0)]
+ [BindColumn("UpdateTime", "更新时间", "")]
+ public DateTime UpdateTime { get => _UpdateTime; set { if (OnPropertyChanging("UpdateTime", value)) { _UpdateTime = value; OnPropertyChanged("UpdateTime"); } } }
+
+ private String _Remark;
+ /// <summary>备注</summary>
+ [DisplayName("备注")]
+ [Description("备注")]
+ [DataObjectField(false, false, true, 500)]
+ [BindColumn("Remark", "备注", "")]
+ public String Remark { get => _Remark; set { if (OnPropertyChanging("Remark", value)) { _Remark = value; OnPropertyChanged("Remark"); } } }
+ #endregion
+
+ #region 获取/设置 字段值
+ /// <summary>获取/设置 字段值</summary>
+ /// <param name="name">字段名</param>
+ /// <returns></returns>
+ public override Object this[String name]
+ {
+ get
+ {
+ switch (name)
+ {
+ case "Id": return _Id;
+ case "Code": return _Code;
+ case "Name": return _Name;
+ case "Type": return _Type;
+ case "Enable": return _Enable;
+ case "UserName": return _UserName;
+ case "Docs": return _Docs;
+ case "Likes": return _Likes;
+ case "Watches": return _Watches;
+ case "Sync": return _Sync;
+ case "Slug": return _Slug;
+ case "Namespace": return _Namespace;
+ case "SyncTime": return _SyncTime;
+ case "CreateUser": return _CreateUser;
+ case "CreateUserID": return _CreateUserID;
+ case "CreateIP": return _CreateIP;
+ case "CreateTime": return _CreateTime;
+ case "UpdateUser": return _UpdateUser;
+ case "UpdateUserID": return _UpdateUserID;
+ case "UpdateIP": return _UpdateIP;
+ case "UpdateTime": return _UpdateTime;
+ case "Remark": return _Remark;
+ default: return base[name];
+ }
+ }
+ set
+ {
+ switch (name)
+ {
+ case "Id": _Id = value.ToInt(); break;
+ case "Code": _Code = Convert.ToString(value); break;
+ case "Name": _Name = Convert.ToString(value); break;
+ case "Type": _Type = Convert.ToString(value); break;
+ case "Enable": _Enable = value.ToBoolean(); break;
+ case "UserName": _UserName = Convert.ToString(value); break;
+ case "Docs": _Docs = value.ToInt(); break;
+ case "Likes": _Likes = value.ToInt(); break;
+ case "Watches": _Watches = value.ToInt(); break;
+ case "Sync": _Sync = value.ToBoolean(); break;
+ case "Slug": _Slug = Convert.ToString(value); break;
+ case "Namespace": _Namespace = Convert.ToString(value); break;
+ case "SyncTime": _SyncTime = value.ToDateTime(); break;
+ case "CreateUser": _CreateUser = Convert.ToString(value); break;
+ case "CreateUserID": _CreateUserID = value.ToInt(); break;
+ case "CreateIP": _CreateIP = Convert.ToString(value); break;
+ case "CreateTime": _CreateTime = value.ToDateTime(); break;
+ case "UpdateUser": _UpdateUser = Convert.ToString(value); break;
+ case "UpdateUserID": _UpdateUserID = value.ToInt(); break;
+ case "UpdateIP": _UpdateIP = Convert.ToString(value); break;
+ case "UpdateTime": _UpdateTime = value.ToDateTime(); break;
+ case "Remark": _Remark = Convert.ToString(value); break;
+ default: base[name] = value; break;
+ }
+ }
+ }
+ #endregion
+
+ #region 字段名
+ /// <summary>取得知识库字段信息的快捷方式</summary>
+ public partial class _
+ {
+ /// <summary>编号</summary>
+ public static readonly Field Id = FindByName("Id");
+
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ public static readonly Field Code = FindByName("Code");
+
+ /// <summary>名称</summary>
+ public static readonly Field Name = FindByName("Name");
+
+ /// <summary>类型</summary>
+ public static readonly Field Type = FindByName("Type");
+
+ /// <summary>启用</summary>
+ public static readonly Field Enable = FindByName("Enable");
+
+ /// <summary>用户</summary>
+ public static readonly Field UserName = FindByName("UserName");
+
+ /// <summary>文章数</summary>
+ public static readonly Field Docs = FindByName("Docs");
+
+ /// <summary>点赞数</summary>
+ public static readonly Field Likes = FindByName("Likes");
+
+ /// <summary>订阅数</summary>
+ public static readonly Field Watches = FindByName("Watches");
+
+ /// <summary>同步。是否自动同步远程内容</summary>
+ public static readonly Field Sync = FindByName("Sync");
+
+ /// <summary>路径</summary>
+ public static readonly Field Slug = FindByName("Slug");
+
+ /// <summary>全路径</summary>
+ public static readonly Field Namespace = FindByName("Namespace");
+
+ /// <summary>同步时间。最后一次同步数据的时间</summary>
+ public static readonly Field SyncTime = FindByName("SyncTime");
+
+ /// <summary>创建者</summary>
+ public static readonly Field CreateUser = FindByName("CreateUser");
+
+ /// <summary>创建人</summary>
+ public static readonly Field CreateUserID = FindByName("CreateUserID");
+
+ /// <summary>创建地址</summary>
+ public static readonly Field CreateIP = FindByName("CreateIP");
+
+ /// <summary>创建时间</summary>
+ public static readonly Field CreateTime = FindByName("CreateTime");
+
+ /// <summary>更新者</summary>
+ public static readonly Field UpdateUser = FindByName("UpdateUser");
+
+ /// <summary>更新人</summary>
+ public static readonly Field UpdateUserID = FindByName("UpdateUserID");
+
+ /// <summary>更新地址</summary>
+ public static readonly Field UpdateIP = FindByName("UpdateIP");
+
+ /// <summary>更新时间</summary>
+ public static readonly Field UpdateTime = FindByName("UpdateTime");
+
+ /// <summary>备注</summary>
+ public static readonly Field Remark = FindByName("Remark");
+
+ static Field FindByName(String name) => Meta.Table.FindByName(name);
+ }
+
+ /// <summary>取得知识库字段名称的快捷方式</summary>
+ public partial class __
+ {
+ /// <summary>编号</summary>
+ public const String Id = "Id";
+
+ /// <summary>编码。路径唯一标识,默认取Slug</summary>
+ public const String Code = "Code";
+
+ /// <summary>名称</summary>
+ public const String Name = "Name";
+
+ /// <summary>类型</summary>
+ public const String Type = "Type";
+
+ /// <summary>启用</summary>
+ public const String Enable = "Enable";
+
+ /// <summary>用户</summary>
+ public const String UserName = "UserName";
+
+ /// <summary>文章数</summary>
+ public const String Docs = "Docs";
+
+ /// <summary>点赞数</summary>
+ public const String Likes = "Likes";
+
+ /// <summary>订阅数</summary>
+ public const String Watches = "Watches";
+
+ /// <summary>同步。是否自动同步远程内容</summary>
+ public const String Sync = "Sync";
+
+ /// <summary>路径</summary>
+ public const String Slug = "Slug";
+
+ /// <summary>全路径</summary>
+ public const String Namespace = "Namespace";
+
+ /// <summary>同步时间。最后一次同步数据的时间</summary>
+ public const String SyncTime = "SyncTime";
+
+ /// <summary>创建者</summary>
+ public const String CreateUser = "CreateUser";
+
+ /// <summary>创建人</summary>
+ public const String CreateUserID = "CreateUserID";
+
+ /// <summary>创建地址</summary>
+ public const String CreateIP = "CreateIP";
+
+ /// <summary>创建时间</summary>
+ public const String CreateTime = "CreateTime";
+
+ /// <summary>更新者</summary>
+ public const String UpdateUser = "UpdateUser";
+
+ /// <summary>更新人</summary>
+ public const String UpdateUserID = "UpdateUserID";
+
+ /// <summary>更新地址</summary>
+ public const String UpdateIP = "UpdateIP";
+
+ /// <summary>更新时间</summary>
+ public const String UpdateTime = "UpdateTime";
+
+ /// <summary>备注</summary>
+ public const String Remark = "Remark";
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj b/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj
new file mode 100644
index 0000000..82e0a34
--- /dev/null
+++ b/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj
@@ -0,0 +1,56 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <AssemblyTitle>语雀Web</AssemblyTitle>
+ <Description>自动语雀内容到本系统,对外呈现内容页面</Description>
+ <Company>新生命开发团队</Company>
+ <Copyright>©2002-2022 NewLife</Copyright>
+ <VersionPrefix>1.0</VersionPrefix>
+ <VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
+ <Version>$(VersionPrefix).$(VersionSuffix)</Version>
+ <FileVersion>$(Version)</FileVersion>
+ <AssemblyVersion>5.0.*</AssemblyVersion>
+ <Deterministic>false</Deterministic>
+ <OutputPath>..\Bin</OutputPath>
+ <GenerateDocumentationFile>True</GenerateDocumentationFile>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <LangVersion>latest</LangVersion>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <IsPackable>true</IsPackable>
+ <PackageId>NewLife.YuqueWeb</PackageId>
+ <Authors>$(Company)</Authors>
+ <PackageProjectUrl>https://www.yuque.com/smartstone/cube</PackageProjectUrl>
+ <PackageIcon>leaf.png</PackageIcon>
+ <RepositoryUrl>https://github.com/NewLifeX/NewLife.Cube</RepositoryUrl>
+ <RepositoryType>git</RepositoryType>
+ <PackageTags>新生命团队;X组件;NewLife;$(AssemblyName)</PackageTags>
+ <PackageReleaseNotes>新增MenuAttribute优化控制器菜单管理;新增魔方管理区域;增强OAuth配置</PackageReleaseNotes>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+ <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
+ <EmbedUntrackedSources>true</EmbedUntrackedSources>
+ <IncludeSymbols>true</IncludeSymbols>
+ <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="NewLife.Cube.Core" Version="5.0.2022.401" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NewLife.YuQue\NewLife.Yuque.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="..\Doc\leaf.png" Link="leaf.png" PackagePath="\" />
+ </ItemGroup>
+
+</Project>
diff --git a/NewLife.YuqueWeb/Properties/launchSettings.json b/NewLife.YuqueWeb/Properties/launchSettings.json
new file mode 100644
index 0000000..d0076b5
--- /dev/null
+++ b/NewLife.YuqueWeb/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:46271",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "NewLife.YuQueWeb": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5210",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/NewLife.YuqueWeb/YuqueService.cs b/NewLife.YuqueWeb/YuqueService.cs
new file mode 100644
index 0000000..6987249
--- /dev/null
+++ b/NewLife.YuqueWeb/YuqueService.cs
@@ -0,0 +1,45 @@
+using System.Reflection;
+using NewLife.Common;
+using NewLife.Cube;
+using NewLife.Log;
+using NewLife.YuqueWeb.Areas.Yuque;
+using XCode.DataAccessLayer;
+
+namespace NewLife.YuQueWeb;
+
+/// <summary>语雀服务</summary>
+public static class YuqueService
+{
+ /// <summary>添加语雀</summary>
+ /// <param name="services"></param>
+ /// <returns></returns>
+ public static IServiceCollection AddYuque(this IServiceCollection services)
+ {
+ using var span = DefaultTracer.Instance?.NewSpan(nameof(AddYuque));
+
+ XTrace.WriteLine("{0} Start 配置语雀 {0}", new String('=', 32));
+ Assembly.GetExecutingAssembly().WriteVersion();
+
+ XTrace.WriteLine("{0} End 配置语雀 {0}", new String('=', 32));
+
+ return services;
+ }
+
+ /// <summary>使用语雀</summary>
+ /// <param name="app"></param>
+ /// <param name="env"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseYuque(this IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ using var span = DefaultTracer.Instance?.NewSpan(nameof(UseYuque));
+
+ XTrace.WriteLine("{0} Start 初始化语雀 {0}", new String('=', 32));
+
+ // 自动检查并添加菜单
+ AreaBase.RegisterArea<YuqueArea>();
+
+ XTrace.WriteLine("{0} End 初始化语雀 {0}", new String('=', 32));
+
+ return app;
+ }
+}
\ No newline at end of file
diff --git a/Test/Test.csproj b/Test/Test.csproj
index a933d03..8c62ad0 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\NewLife.YuQue\NewLife.YuQue.csproj" />
+ <ProjectReference Include="..\NewLife.YuQue\NewLife.Yuque.csproj" />
</ItemGroup>
</Project>
diff --git a/XUnitTest/XUnitTest.csproj b/XUnitTest/XUnitTest.csproj
index 8352ff0..e11ff0c 100644
--- a/XUnitTest/XUnitTest.csproj
+++ b/XUnitTest/XUnitTest.csproj
@@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\NewLife.YuQue\NewLife.YuQue.csproj" />
+ <ProjectReference Include="..\NewLife.YuQue\NewLife.Yuque.csproj" />
</ItemGroup>
</Project>
diff --git a/YqWeb/appsettings.Development.json b/YqWeb/appsettings.Development.json
new file mode 100644
index 0000000..770d3e9
--- /dev/null
+++ b/YqWeb/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "DetailedErrors": true,
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/YqWeb/appsettings.json b/YqWeb/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/YqWeb/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/YqWeb/Program.cs b/YqWeb/Program.cs
new file mode 100644
index 0000000..f11e9ed
--- /dev/null
+++ b/YqWeb/Program.cs
@@ -0,0 +1,21 @@
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.AddRazorPages();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (!app.Environment.IsDevelopment())
+{
+ app.UseExceptionHandler("/Error");
+}
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.UseAuthorization();
+
+app.MapRazorPages();
+
+app.Run();
diff --git a/YqWeb/Properties/launchSettings.json b/YqWeb/Properties/launchSettings.json
new file mode 100644
index 0000000..b8521f2
--- /dev/null
+++ b/YqWeb/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:33992",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "YQWeb": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5153",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/YqWeb/YqWeb.csproj b/YqWeb/YqWeb.csproj
new file mode 100644
index 0000000..f8c2544
--- /dev/null
+++ b/YqWeb/YqWeb.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NewLife.YuQueWeb\NewLife.YuqueWeb.csproj" />
+ </ItemGroup>
+
+</Project>