diff --git a/NewLife.YuQue/YuqueClient.cs b/NewLife.YuQue/YuqueClient.cs
index 2463a66..5326b57 100644
--- a/NewLife.YuQue/YuqueClient.cs
+++ b/NewLife.YuQue/YuqueClient.cs
@@ -623,26 +623,28 @@ namespace NewLife.YuQue
/// 获取一个仓库的文档列表
/// </summary>
/// <param name="bookName">仓库路径</param>
+ /// <param name="offset">用于分页,效果类似 MySQL 的 limit offset,一页 20 条</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
- public virtual async Task<Document[]> GetDocuments(String bookName)
+ public virtual async Task<Document[]> GetDocuments(String bookName, Int32 offset = 0)
{
if (bookName.IsNullOrEmpty()) throw new ArgumentNullException(nameof(bookName));
- return await GetAsync<Document[]>($"/repos/{bookName}/docs");
+ return await GetAsync<Document[]>($"/repos/{bookName}/docs", new { offset });
}
/// <summary>
/// 获取一个仓库的文档列表
/// </summary>
/// <param name="bookId">仓库编号</param>
+ /// <param name="offset">用于分页,效果类似 MySQL 的 limit offset,一页 20 条</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
- public virtual async Task<Document[]> GetDocuments(Int32 bookId)
+ public virtual async Task<Document[]> GetDocuments(Int32 bookId, Int32 offset = 0)
{
if (bookId <= 0) throw new ArgumentNullException(nameof(bookId));
- return await GetAsync<Document[]>($"/repos/{bookId}/docs");
+ return await GetAsync<Document[]>($"/repos/{bookId}/docs", new { offset });
}
/// <summary>
diff --git a/NewLife.YuqueWeb/Areas/Yuque/Controllers/BookController.cs b/NewLife.YuqueWeb/Areas/Yuque/Controllers/BookController.cs
index 43b7ed1..15065f7 100644
--- a/NewLife.YuqueWeb/Areas/Yuque/Controllers/BookController.cs
+++ b/NewLife.YuqueWeb/Areas/Yuque/Controllers/BookController.cs
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using NewLife.Cube;
using NewLife.Web;
-using NewLife.YuQue;
+using NewLife.YuqueWeb.Services;
using NewLife.YuQueWeb.Entity;
using XCode.Membership;
@@ -11,6 +11,10 @@ namespace NewLife.YuqueWeb.Areas.Yuque.Controllers
[Menu(90, true, Icon = "fa-tachometer")]
public class BookController : EntityController<Book>
{
+ private readonly BookService _bookService;
+
+ public BookController(BookService bookService) => _bookService = bookService;
+
protected override IEnumerable<Book> Search(Pager p)
{
var enable = p["enable"]?.ToBoolean();
@@ -30,14 +34,10 @@ namespace NewLife.YuqueWeb.Areas.Yuque.Controllers
{
var count = 0;
var ids = GetRequest("keys").SplitAsInt();
- if (ids.Length > 0)
- foreach (var id in ids)
- {
- var team = Book.FindById(id);
- if (team != null)
- {
- }
- }
+ foreach (var id in ids.OrderBy(e => e))
+ {
+ count += await _bookService.Sync(id);
+ }
return JsonRefresh($"共刷新[{count}]个知识库");
}
@@ -47,65 +47,7 @@ namespace NewLife.YuqueWeb.Areas.Yuque.Controllers
[EntityAuthorize(PermissionFlags.Insert)]
public async Task<ActionResult> ScanAll()
{
- var count = 0;
-
- // 获取令牌
- var p = Parameter.GetOrAdd(0, "Yuque", "Token");
- if (p.Value.IsNullOrEmpty())
- {
- if (p.Remark.IsNullOrEmpty())
- {
- p.Remark = "访问语雀的令牌,账户设置/Token,https://www.yuque.com/settings/tokens";
- p.Update();
- }
-
- throw new Exception("未设置令牌![系统管理/字典参数/Yuque/Token]");
- }
-
- var client = new YuqueClient { Token = p.Value };
- var user = await client.GetUser();
-
- var list = Book.FindAll();
- var offset = 0;
- while (true)
- {
- var repos = await client.GetRepos(user.Id, "all", offset);
- if (repos.Length == 0) break;
-
- foreach (var repo in repos)
- {
- var book = list.FirstOrDefault(e => e.Id == repo.Id || e.Slug == repo.Slug);
- if (book == null)
- {
- book = new Book
- {
- Id = repo.Id,
- Name = repo.Name,
- Slug = repo.Slug,
- Code = repo.Slug,
- Enable = true
- };
- book.Insert();
-
- list.Add(book);
- }
-
- book.Name = repo.Name;
- book.Type = repo.Type;
- book.UserName = repo.User?.Name;
- book.Docs = repo.Items;
- book.Likes = repo.Likes;
- book.Watches = repo.Watches;
- book.Namespace = repo.Namespace;
- book.Remark = repo.Description;
- book.SyncTime = DateTime.Now;
-
- book.Update();
- }
-
- count += repos.Length;
- offset += repos.Length;
- }
+ var count = await _bookService.ScanAll();
return JsonRefresh($"共扫描[{count}]个知识库");
}
diff --git a/NewLife.YuqueWeb/Areas/Yuque/Views/Book/_List_Toolbar_Batch.cshtml b/NewLife.YuqueWeb/Areas/Yuque/Views/Book/_List_Toolbar_Batch.cshtml
index d0ea3c4..a8ff85c 100644
--- a/NewLife.YuqueWeb/Areas/Yuque/Views/Book/_List_Toolbar_Batch.cshtml
+++ b/NewLife.YuqueWeb/Areas/Yuque/Views/Book/_List_Toolbar_Batch.cshtml
@@ -11,9 +11,9 @@
@if (set.EnableSelect)
{
<button type="button" class="btn btn-purple btn-sm" data-action="action" data-url="@Url.Action("SyncRepo")" data-fields="keys" disabled>
- 同步知识库
+ 同步文章列表
</button>
<button type="button" class="btn btn-purple btn-sm" data-action="action" data-url="@Url.Action("ScanAll")" >
- 扫描全部知识库
+ 扫描知识库
</button>
}
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/Entity/Config/Core.config b/NewLife.YuqueWeb/Entity/Config/Core.config
new file mode 100644
index 0000000..1a42483
--- /dev/null
+++ b/NewLife.YuqueWeb/Entity/Config/Core.config
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Core>
+ <!--全局调试。XTrace.Debug-->
+ <Debug>true</Debug>
+ <!--日志等级。只输出大于等于该级别的日志,All/Debug/Info/Warn/Error/Fatal,默认Info-->
+ <LogLevel>Info</LogLevel>
+ <!--文件日志目录。默认Log子目录,web上一级Log-->
+ <LogPath>Log</LogPath>
+ <!--日志文件上限。超过上限后拆分新日志文件,默认10MB,0表示不限制大小-->
+ <LogFileMaxBytes>10</LogFileMaxBytes>
+ <!--日志文件备份。超过备份数后,最旧的文件将被删除,默认100,0表示不限制个数-->
+ <LogFileBackups>100</LogFileBackups>
+ <!--日志文件格式。默认{0:yyyy_MM_dd}.log,支持日志等级如 {1}_{0:yyyy_MM_dd}.log-->
+ <LogFileFormat>{0:yyyy_MM_dd}.log</LogFileFormat>
+ <!--网络日志。本地子网日志广播udp://255.255.255.255:514,或者http://xxx:80/log-->
+ <NetworkLog></NetworkLog>
+ <!--数据目录。本地数据库目录,默认Data子目录,web上一级Data-->
+ <DataPath>Data</DataPath>
+ <!--备份目录。备份数据库时存放的目录,默认Backup子目录,web上一级Backup-->
+ <BackupPath>Backup</BackupPath>
+ <!--插件目录-->
+ <PluginPath>Plugins</PluginPath>
+ <!--插件服务器。将从该网页上根据关键字分析链接并下载插件-->
+ <PluginServer>http://x.newlifex.com/</PluginServer>
+</Core>
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/Entity/Config/XCode.config b/NewLife.YuqueWeb/Entity/Config/XCode.config
new file mode 100644
index 0000000..149162e
--- /dev/null
+++ b/NewLife.YuqueWeb/Entity/Config/XCode.config
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XCode>
+ <!--调试-->
+ <Debug>true</Debug>
+ <!--输出SQL。是否输出SQL语句,默认启用-->
+ <ShowSQL>true</ShowSQL>
+ <!--SQL目录。设置SQL输出的单独目录,默认为空,SQL输出到当前日志中。生产环境建议输出到站点外单独的SqlLog目录-->
+ <SQLPath></SQLPath>
+ <!--SQL执行时间。跟踪SQL执行时间,大于该阀值将输出日志,默认1000毫秒-->
+ <TraceSQLTime>1000</TraceSQLTime>
+ <!--SQL最大长度。输出日志时的SQL最大长度,超长截断,默认4096,不截断用0-->
+ <SQLMaxLength>4096</SQLMaxLength>
+ <!--参数化添删改查。默认关闭-->
+ <UseParameter>false</UseParameter>
+ <!--命令超时。查询执行超时时间,默认0秒不限制-->
+ <CommandTimeout>0</CommandTimeout>
+ <!--失败重试。执行命令超时后的重试次数,默认0不重试-->
+ <RetryOnFailure>0</RetryOnFailure>
+ <!--数据层缓存。根据sql做缓存,默认0秒-->
+ <DataCacheExpire>0</DataCacheExpire>
+ <!--实体缓存过期。整表缓存实体列表,默认10秒-->
+ <EntityCacheExpire>10</EntityCacheExpire>
+ <!--单对象缓存过期。按主键缓存实体,默认10秒-->
+ <SingleCacheExpire>10</SingleCacheExpire>
+ <!--扩展属性过期。扩展属性Extends缓存,默认10秒-->
+ <ExtendExpire>10</ExtendExpire>
+ <!--反向工程。Off 关闭;ReadOnly 只读不执行;On 打开,仅新建;Full 完全,修改删除-->
+ <Migration>On</Migration>
+ <!--表名称、字段名大小写格式。Default 根据模型生成;Upper 全大写;Lower 全小写;Underline下划线-->
+ <NameFormat>Default</NameFormat>
+</XCode>
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/Entity/Model.xml b/NewLife.YuqueWeb/Entity/Model.xml
index 49e087d..bfd0d80 100644
--- a/NewLife.YuqueWeb/Entity/Model.xml
+++ b/NewLife.YuqueWeb/Entity/Model.xml
@@ -2,7 +2,7 @@
<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="Id" DataType="Int32" 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="类型" />
@@ -32,9 +32,9 @@
</Table>
<Table Name="Document" Description="文档。文档内容">
<Columns>
- <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+ <Column Name="Id" DataType="Int32" PrimaryKey="True" Description="编号" />
<Column Name="Code" DataType="String" Description="编码。路径唯一标识,默认取Slug" />
- <Column Name="Title" DataType="String" Master="True" Nullable="False" Description="标题" />
+ <Column Name="Title" DataType="String" Master="True" Length="200" Nullable="False" Description="标题" />
<Column Name="BookId" DataType="Int32" Description="知识库" />
<Column Name="Enable" DataType="Boolean" Description="启用" />
<Column Name="UserName" DataType="String" Description="用户" />
@@ -48,7 +48,10 @@
<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="Cover" DataType="String" Length="200" Description="封面" />
+ <Column Name="Slug" DataType="String" Description="路径" />
+ <Column Name="Sync" DataType="Boolean" 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" />
diff --git a/NewLife.YuqueWeb/Entity/YuQue.htm b/NewLife.YuqueWeb/Entity/YuQue.htm
index 20d9e6a..4d751b5 100644
--- a/NewLife.YuqueWeb/Entity/YuQue.htm
+++ b/NewLife.YuqueWeb/Entity/YuQue.htm
@@ -55,7 +55,7 @@
<td>Int32</td>
<td></td>
<td></td>
- <td title="自增">AI</td>
+ <td title="主键">PK</td>
<td>N</td>
<td></td>
</tr>
@@ -314,7 +314,7 @@
<td>Int32</td>
<td></td>
<td></td>
- <td title="自增">AI</td>
+ <td title="主键">PK</td>
<td>N</td>
<td></td>
</tr>
@@ -334,7 +334,7 @@
<td>Title</td>
<td>标题</td>
<td>String</td>
- <td>50</td>
+ <td>200</td>
<td></td>
<td></td>
<td>N</td>
@@ -488,6 +488,17 @@
<td>Cover</td>
<td>封面</td>
<td>String</td>
+ <td>200</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Slug</td>
+ <td>路径</td>
+ <td>String</td>
<td>50</td>
<td></td>
<td></td>
@@ -496,6 +507,28 @@
</tr>
<tr>
+ <td>Sync</td>
+ <td>同步</td>
+ <td>Boolean</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td>N</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>
diff --git "a/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs" "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs"
index b68f759..442fc6a 100644
--- "a/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs"
+++ "b/NewLife.YuqueWeb/Entity/\346\226\207\346\241\243.cs"
@@ -25,7 +25,7 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>编号</summary>
[DisplayName("编号")]
[Description("编号")]
- [DataObjectField(true, true, false, 0)]
+ [DataObjectField(true, false, false, 0)]
[BindColumn("Id", "编号", "")]
public Int32 Id { get => _Id; set { if (OnPropertyChanging("Id", value)) { _Id = value; OnPropertyChanged("Id"); } } }
@@ -41,7 +41,7 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>标题</summary>
[DisplayName("标题")]
[Description("标题")]
- [DataObjectField(false, false, false, 50)]
+ [DataObjectField(false, false, false, 200)]
[BindColumn("Title", "标题", "", Master = true)]
public String Title { get => _Title; set { if (OnPropertyChanging("Title", value)) { _Title = value; OnPropertyChanged("Title"); } } }
@@ -153,10 +153,34 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>封面</summary>
[DisplayName("封面")]
[Description("封面")]
- [DataObjectField(false, false, true, 50)]
+ [DataObjectField(false, false, true, 200)]
[BindColumn("Cover", "封面", "")]
public String Cover { get => _Cover; set { if (OnPropertyChanging("Cover", value)) { _Cover = value; OnPropertyChanged("Cover"); } } }
+ 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 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 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("创建者")]
@@ -257,6 +281,9 @@ namespace NewLife.YuQueWeb.Entity
case "FirstPublishTime": return _FirstPublishTime;
case "WordCount": return _WordCount;
case "Cover": return _Cover;
+ case "Slug": return _Slug;
+ case "Sync": return _Sync;
+ case "SyncTime": return _SyncTime;
case "CreateUser": return _CreateUser;
case "CreateUserID": return _CreateUserID;
case "CreateIP": return _CreateIP;
@@ -290,6 +317,9 @@ namespace NewLife.YuQueWeb.Entity
case "FirstPublishTime": _FirstPublishTime = value.ToDateTime(); break;
case "WordCount": _WordCount = value.ToInt(); break;
case "Cover": _Cover = Convert.ToString(value); break;
+ case "Slug": _Slug = Convert.ToString(value); break;
+ case "Sync": _Sync = value.ToBoolean(); 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;
@@ -360,6 +390,15 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>封面</summary>
public static readonly Field Cover = FindByName("Cover");
+ /// <summary>路径</summary>
+ public static readonly Field Slug = FindByName("Slug");
+
+ /// <summary>同步。是否自动同步远程内容</summary>
+ public static readonly Field Sync = FindByName("Sync");
+
+ /// <summary>同步时间。最后一次同步数据的时间</summary>
+ public static readonly Field SyncTime = FindByName("SyncTime");
+
/// <summary>创建者</summary>
public static readonly Field CreateUser = FindByName("CreateUser");
@@ -444,6 +483,15 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>封面</summary>
public const String Cover = "Cover";
+ /// <summary>路径</summary>
+ public const String Slug = "Slug";
+
+ /// <summary>同步。是否自动同步远程内容</summary>
+ public const String Sync = "Sync";
+
+ /// <summary>同步时间。最后一次同步数据的时间</summary>
+ public const String SyncTime = "SyncTime";
+
/// <summary>创建者</summary>
public const String CreateUser = "CreateUser";
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"
index e0c5f95..0d2fa78 100644
--- "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"
@@ -24,7 +24,7 @@ namespace NewLife.YuQueWeb.Entity
/// <summary>编号</summary>
[DisplayName("编号")]
[Description("编号")]
- [DataObjectField(true, true, false, 0)]
+ [DataObjectField(true, false, false, 0)]
[BindColumn("Id", "编号", "")]
public Int32 Id { get => _Id; set { if (OnPropertyChanging("Id", value)) { _Id = value; OnPropertyChanged("Id"); } } }
diff --git a/NewLife.YuqueWeb/Services/BookService.cs b/NewLife.YuqueWeb/Services/BookService.cs
new file mode 100644
index 0000000..4f67e37
--- /dev/null
+++ b/NewLife.YuqueWeb/Services/BookService.cs
@@ -0,0 +1,164 @@
+using NewLife.Log;
+using NewLife.YuQue;
+using NewLife.YuQueWeb.Entity;
+using XCode.Membership;
+
+namespace NewLife.YuqueWeb.Services;
+
+/// <summary>
+/// 知识库服务
+/// </summary>
+public class BookService
+{
+ private readonly ITracer _tracer;
+
+ /// <summary>
+ /// 实例化知识库服务
+ /// </summary>
+ /// <param name="tracer"></param>
+ public BookService(ITracer tracer)
+ {
+ _tracer = tracer;
+ }
+
+ /// <summary>
+ /// 扫描发现所有知识库
+ /// </summary>
+ /// <returns></returns>
+ public async Task<Int32> ScanAll()
+ {
+ var count = 0;
+ var token = GetToken();
+
+ var client = new YuqueClient { Token = token, Log = XTrace.Log, Tracer = _tracer };
+ var user = await client.GetUser();
+
+ var list = Book.FindAll();
+ var offset = 0;
+ while (true)
+ {
+ var repos = await client.GetRepos(user.Id, "all", offset);
+ if (repos.Length == 0) break;
+
+ foreach (var item in repos)
+ {
+ var book = list.FirstOrDefault(e => e.Id == item.Id || e.Slug == item.Slug);
+ if (book == null)
+ {
+ book = new Book
+ {
+ Id = item.Id,
+ Name = item.Name,
+ Slug = item.Slug,
+ Code = item.Slug,
+ Enable = true,
+ Sync = true,
+ };
+ book.Insert();
+
+ list.Add(book);
+ }
+
+ book.Name = item.Name;
+ book.Type = item.Type;
+ book.UserName = item.User?.Name;
+ book.Docs = item.Items;
+ book.Likes = item.Likes;
+ book.Watches = item.Watches;
+ book.Namespace = item.Namespace;
+ book.Remark = item.Description;
+ book.SyncTime = DateTime.Now;
+ book.CreateTime = item.CreateTime;
+ book.UpdateTime = item.UpdateTime;
+
+ book.Update();
+ }
+
+ count += repos.Length;
+ offset += repos.Length;
+ }
+
+ return count;
+ }
+
+ String GetToken()
+ {
+ // 获取令牌
+ var p = Parameter.GetOrAdd(0, "Yuque", "Token");
+ if (p.Value.IsNullOrEmpty())
+ {
+ if (p.Remark.IsNullOrEmpty())
+ {
+ p.Remark = "访问语雀的令牌,账户设置/Token,https://www.yuque.com/settings/tokens";
+ p.Update();
+ }
+
+ throw new Exception("未设置令牌![系统管理/字典参数/Yuque/Token]");
+ }
+
+ return p.Value;
+ }
+
+ /// <summary>
+ /// 同步指定知识库之下的文章列表
+ /// </summary>
+ /// <param name="bookId"></param>
+ /// <returns></returns>
+ public async Task<Int32> Sync(Int32 bookId)
+ {
+ var book = Book.FindById(bookId);
+ if (book == null || !book.Sync) return 0;
+
+ var token = GetToken();
+ var client = new YuqueClient { Token = token, Log = XTrace.Log, Tracer = _tracer };
+
+ var count = 0;
+ var offset = 0;
+ while (true)
+ {
+ var list = await client.GetDocuments(book.Id, offset);
+ if (list.Length == 0) break;
+
+ foreach (var item in list)
+ {
+ var doc = Document.FindByCode(item.Slug);
+ if (doc == null)
+ {
+ doc = new Document
+ {
+ Id = item.Id,
+ Code = item.Slug,
+ Title = item.Title,
+ BookId = bookId,
+ Slug = item.Slug,
+ Enable = true,
+ Sync = true
+ };
+ doc.Insert();
+ }
+
+ doc.UserName = item.LastEditor?.Name;
+ doc.Format = item.Format;
+ //doc.Hits = item.Hits;
+ doc.Likes = item.Likes;
+ doc.Comments = item.Comments;
+ doc.WordCount = item.WordCount;
+ doc.Cover = item.Cover;
+ doc.Remark = item.Description;
+
+ doc.SyncTime = DateTime.Now;
+ doc.PublishTime = item.PublishTime;
+ doc.PublishTime = item.FirstPublishTime;
+ doc.CreateTime = item.CreateTime;
+ doc.UpdateTime = item.UpdateTime;
+
+ doc.Update();
+ }
+
+ count += list.Length;
+ offset += list.Length;
+ }
+
+ return count;
+ }
+}
\ No newline at end of file
diff --git a/NewLife.YuqueWeb/YuqueService.cs b/NewLife.YuqueWeb/YuqueService.cs
index 2f2759e..60600de 100644
--- a/NewLife.YuqueWeb/YuqueService.cs
+++ b/NewLife.YuqueWeb/YuqueService.cs
@@ -2,6 +2,7 @@
using NewLife.Cube;
using NewLife.Log;
using NewLife.YuqueWeb.Areas.Yuque;
+using NewLife.YuqueWeb.Services;
namespace NewLife.YuQueWeb;
@@ -18,6 +19,8 @@ public static class YuqueService
XTrace.WriteLine("{0} Start 配置语雀 {0}", new String('=', 32));
Assembly.GetExecutingAssembly().WriteVersion();
+ services.AddSingleton<BookService>();
+
XTrace.WriteLine("{0} End 配置语雀 {0}", new String('=', 32));
return services;