更新说明文档
大石头 authored at 2022-04-16 15:30:05
15.18 KiB
YuQue
using NewLife;
using NewLife.Log;
using NewLife.YuqueWeb.Entity;
using XCode;

// 本地开发测试数据初始化工具(幂等,可重复运行)
// 输出目录与 YqWeb 一致(Bin/Web),XCode 自动连接 Bin/Web/Data/Yuque.db
XTrace.UseConsole();
XTrace.WriteLine("开始初始化测试数据……");

#region Nav 头部树补全
var header = Nav.Root.FindByPath("头部") ?? Nav.Root.Add("头部");

// 文档下拉
var doc = header.Childs.FirstOrDefault(e => e.Name == "文档");
if (doc == null)
{
    doc = header.Add("文档");
    doc.NewWindow = false;
    doc.Sort = 20;
    doc.Update();
}
if (doc.Childs.Count == 0)
{
    doc.Add("核心组件(NewLife全家桶)", "/core");
    doc.Add("大数据中间件XCode", "/xcode");
    doc.Add("魔方Cube(快速开发平台)", "/cube");
    doc.Add("精心匠造(平台级产品)", "/blood");
    doc.Add("物联网&嵌入式", "/iot");
    doc.Add("计算机技术", "/tech");
    doc.Add("事件活动", "/events");
}

if (header.Childs.All(e => e.Name != "开源项目"))
{
    var n = header.Add("开源项目", "https://github.com/NewLifeX");
    n.NewWindow = true;
    n.Sort = 30;
    n.Update();
}
if (header.Childs.All(e => e.Name != "AI问答"))
{
    var n = header.Add("AI问答", "https://ai.newlifex.com/");
    n.NewWindow = true;
    n.Sort = 40;
    n.Update();
}

// 头部子项排序校正(EntityTree Childs 不保证按 Sort,前台已显式 OrderBy)
var order = new (String name, Int32 sort)[]
{
    ("首页", 10), ("文档", 20), ("开源项目", 30), ("AI问答", 40), ("关于我们", 50),
};
foreach (var (name, sort) in order)
{
    var c = header.Childs.FirstOrDefault(e => e.Name == name);
    if (c != null)
    {
        c.Sort = sort;
        c.Update();
    }
}

// 隐藏 InitData 遗留的"产品方案"占位(# 链接)
var legacy = header.Childs.FirstOrDefault(e => e.Name == "产品方案");
if (legacy != null)
{
    legacy.Enable = false;
    legacy.Update();
}
XTrace.WriteLine("Nav 头部树 OK");
#endregion

#region Nav 推荐树(首页核心产品)
var rec = Nav.Root.FindByPath("推荐") ?? Nav.Root.Add("推荐");

// 每次运行强制按名称设置,避免 EntityTree Add 后 Update 的 Sort 不可靠导致错位
var projDefs = new (String name, String url, String remark, Int32 sort)[]
{
    ("星尘 Stardust", "/blood/stardust", "轻量级可观测 APM、服务守护、配置中心三位一体,为 .NET 服务提供全链路监控与治理能力。", 100),
    ("蚂蚁调度 AntJob", "/blood/antjob", "分布式、灵活、部署便捷,多模式多样化适配各类数据。日均 10 亿级数据实时计算,延迟小于 1 分钟。", 90),
    ("魔方 Cube", "/cube", "支持多种 SSO 登录、完整验证流程,零配置快速完成网站管理后台界面功能搭建。", 80),
    ("星语 StarChat", "/blood/xingyu", "新生命团队面向 AI 时代的智能交互产品,正在精心打造中,敬请期待。", 70),
};
foreach (var (name, url, remark, sort) in projDefs)
{
    var c = rec.Childs.FirstOrDefault(e => e.Name == name);
    if (c == null) c = rec.Add(name, url);
    c.Remark = remark;
    c.Sort = sort;
    c.Enable = true;
    c.Update();
}
XTrace.WriteLine("Nav 推荐树 OK");
#endregion

#region Book 知识库(分类)
Book AddBook(String code, String name, String remark, Int32 docs, DateTime update)
{
    var b = Book.FindByCode(code);
    if (b == null)
    {
        // Book.Id 非自增主键(映射语雀 Id),需手动分配
        var maxId = Book.FindAll().DefaultIfEmpty().Max(e => e?.Id ?? 0);
        b = new Book { Id = maxId + 1, Code = code, Enable = true, Public = true, Sync = false };
    }
    b.Name = name;
    b.Remark = remark;
    b.Docs = docs;
    b.Sort = docs;
    b.ContentUpdateTime = update;
    b.UpdateTime = update;
    b.Save();
    return b;
}

var bCore = AddBook("core", "核心组件(NewLife全家桶)", "NewLife.Core 核心组件库,涵盖网络、RPC、日志、配置、序列化、缓存、多线程等模块使用指南", 86, DateTime.Parse("2026-03-07"));
var bXcode = AddBook("xcode", "大数据中间件XCode", "支持多种主流关系数据库、动态分库分表、自动建表的数据中间件", 42, DateTime.Parse("2026-02-18"));
var bCube = AddBook("cube", "魔方Cube(快速开发平台)", "Web 快速开发框架,内置权限、菜单、代码生成,快速搭建管理后台", 35, DateTime.Parse("2026-01-30"));
var bBlood = AddBook("blood", "精心匠造(平台级产品)", "星尘、蚂蚁调度、魔方等平台级产品的团队介绍与使用文档", 18, DateTime.Parse("2026-04-23"));
var bIot = AddBook("iot", "物联网&嵌入式", "MQTT、Modbus 等物联网协议与嵌入式软硬件开发实践", 27, DateTime.Parse("2026-03-15"));
var bTech = AddBook("tech", "计算机技术", "编程语言、算法、架构设计等通用计算机技术分享", 51, DateTime.Parse("2026-02-05"));
var bEvents = AddBook("events", "事件活动", "新生命团队线下活动、技术沙龙、开源社区动态", 12, DateTime.Parse("2026-01-12"));
XTrace.WriteLine("Book 分类 OK");
#endregion

#region Document 文档
Document AddDoc(Book book, String code, String title, String summary, String html, DateTime pub, Int32 hits = 1000, Boolean top = false, String format = null)
{
    var d = Document.FindByBookAndCode(book.Id, code);
    if (d == null)
    {
        // Document.Id 非自增主键(映射语雀 Id),需手动分配
        var maxId = Document.FindAll().DefaultIfEmpty().Max(e => e?.Id ?? 0);
        d = new Document { Id = maxId + 1, BookId = book.Id, Code = code, Enable = true, Sync = false };
    }
    d.Title = title;
    d.Summary = summary;
    d.BodyHtml = html;
    d.Html = html;
    d.Public = true;
    d.Status = true;
    d.Format = format;
    d.Sort = top ? 100 : 0;
    d.UserName = "大石头";
    d.Hits = hits;
    d.TotalHits = hits;
    d.PublishTime = pub;
    d.ContentUpdateTime = pub;
    d.UpdateTime = pub;
    d.Save();
    return d;
}

// core 分类文章
AddDoc(bCore, "nav", "NewLife 核心组件导航目录",
    "收集整理 NewLife 核心组件中最常用的文章,作为新手上路的导航入口,涵盖网络、日志、配置、缓存等所有核心模块的快速索引。",
    "<h2 id=\"s1\">导航目录</h2><p>本文档收集整理 NewLife 核心组件中最常用的文章,涵盖网络、日志、配置、缓存、序列化等所有核心模块。</p><h2 id=\"s2\">网络模块</h2><p>实测单点最高 <strong>400 万</strong>长连接,RPC 单点最高 <strong>2266 万</strong> TPS。</p>",
    DateTime.Parse("2024-08-24"), 37289, true);

AddDoc(bCore, "advanced_binary", "高级二进制序列化",
    "本文讲解 NewLife 中二进制序列化的高级应用,是面向高级工程师和 AI 的零分配协议编解码架构指南。核心目标:协议消息在 IPacket 数据包、NetworkStream 网络流、FileStream 文件流以及加密/压缩包装流中编解码时,全程零内存分配。",
    "<h2 id=\"s1\">为什么需要二进制序列化</h2><p>在性能敏感场景中,JSON/XML 序列化的开销难以接受,需要零分配的二进制编解码方案。</p><blockquote>核心目标:全程零内存分配。</blockquote><h2 id=\"s2\">快速开始</h2><p>使用 <code>BinaryTree</code> 进行编解码:</p><pre><code>using var reader = new SpanReader(data);\nvar id = reader.ReadInt32();\nvar name = reader.ReadString();</code></pre><h2 id=\"s3\">最佳实践</h2><ul><li><strong>零拷贝</strong>:优先使用 Span 避免复制</li><li><strong>版本兼容</strong>:协议头携带版本号</li></ul>",
    DateTime.Parse("2026-03-07"), 3240);

AddDoc(bCore, "buffers", "高性能缓冲区操作类 Buffers",
    "本文档库包含了 NewLife.Core 中四个核心的高性能缓冲区操作类的详细说明和使用指南。",
    "<h2 id=\"s1\">概述</h2><p>NewLife.Core 提供四个高性能缓冲区操作类,覆盖读写、池化与边界搜索场景。</p><h2 id=\"s2\">SpanHelper</h2><p>提供 Span 常用扩展方法,专注于高性能、零分配的操作。</p>",
    DateTime.Parse("2025-09-25"), 1860);

AddDoc(bCore, "span_reader", "字节流解析器 SpanReader",
    "SpanReader 是一个高性能的只读字节流解析器,用于零分配地从内存缓冲区或数据流中读取二进制数据。它是一个 ref struct,专为解析网络协议帧而设计。",
    "<h2 id=\"s1\">简介</h2><p>SpanReader 是高性能只读字节流解析器,零分配读取二进制数据。</p>",
    DateTime.Parse("2025-09-25"), 1508);

AddDoc(bCore, "ipacket", "IPacket 数据包",
    "IPacket 是 NewLife.Core 中的核心数据包接口,用于高性能网络编程和协议解析。它采用内存共享理念,通过对象池和链式结构实现零拷贝数据处理。",
    "<h2 id=\"s1\">设计理念</h2><p>内存共享 + 对象池 + 链式结构,显著降低内存分配和 GC 压力。</p>",
    DateTime.Parse("2025-09-24"), 2106);

AddDoc(bCore, "tracer", "链路追踪 Tracer:可观测性埋点规范与实践",
    "可观测是衡量现代应用质量的核心指标之一,我们设计了 ITracer/ISpan 这一套链路追踪规范。开发者可以根据该规范来编写各种关键性代码埋点。",
    "<h2 id=\"s1\">为什么需要链路追踪</h2><p>在微服务与分布式时代,传统日志无法串联整个调用链。</p><blockquote>链路追踪把散落各处的日志串成一条完整的调用链。</blockquote><h2 id=\"s2\">核心概念</h2><table><thead><tr><th>概念</th><th>说明</th></tr></thead><tbody><tr><td>ITracer</td><td>全局追踪入口</td></tr><tr><td>ISpan</td><td>一次方法/请求的耗时与状态</td></tr></tbody></table><h2 id=\"s3\">快速开始</h2><pre><code>using var span = _tracer?.NewSpan(\"GetName\", id);</code></pre>",
    DateTime.Parse("2024-12-16"), 5102);

AddDoc(bCore, "icacheprovider", "缓存架构 ICacheProvider",
    "在 ICache 基础上,进一步封装了缓存架构 ICacheProvider,用于业务应用中常用的分布式缓存、本地缓存和消息队列。",
    "<h2 id=\"s1\">简介</h2><p>强烈推荐使用 ICacheProvider 替代 ICache。</p>",
    DateTime.Parse("2024-12-17"), 4330);

AddDoc(bCore, "no_log", "如何关闭日志输出",
    "为了方便分析问题,NewLife 所有组件都带有日志输出。日志默认输出到 Log 文件夹,本文介绍如何在稳定后关闭控制台与文本日志。",
    "<h2 id=\"s1\">问题</h2><p>项目稳定后不想输出控制台日志或文本日志。</p><h2 id=\"s2\">解决方案</h2><p>配置 <code>Log=XTrace.Log</code> 级别,或关闭 <code>XTrace.UseConsole</code>。</p>",
    DateTime.Parse("2024-04-21"), 892);

// xcode 分类
AddDoc(bXcode, "model", "XCode 数据建模入门",
    "使用 Model.xml 设计表结构,通过 xcode 命令生成实体类,自动建表、自动维护。",
    "<h2 id=\"s1\">Model.xml</h2><p>表结构设计文件,支持字段、索引、关系配置。</p><h2 id=\"s2\">实体生成</h2><pre><code>xcode model.xml</code></pre>",
    DateTime.Parse("2026-02-18"), 2310);

AddDoc(bXcode, "multi_db", "多数据库支持",
    "XCode 支持多种主流关系数据库,同一套实体代码可在 SQLite/MySQL/SqlServer/Oracle 间切换。",
    "<h2 id=\"s1\">数据库抽象</h2><p>通过连接名与数据库类型抽象,实现多库无缝切换。</p>",
    DateTime.Parse("2026-01-20"), 1780);

// cube 分类
AddDoc(bCube, "start", "魔方 Cube 快速开始",
    "使用 NewLife.Cube 快速搭建网站管理后台,零配置完成界面功能搭建。",
    "<h2 id=\"s1\">安装</h2><pre><code>dotnet new install NewLife.Templates\ndotnet new cube -n MyWeb</code></pre>",
    DateTime.Parse("2026-01-30"), 3120);

AddDoc(bCube, "permission", "魔方权限体系",
    "Cube 内置完整权限体系:菜单、角色、用户、PermissionFlags 六种权限位。",
    "<h2 id=\"s1\">权限位</h2><p>Detail/Insert/Update/Delete/Approve/Export 六种权限。</p>",
    DateTime.Parse("2025-12-20"), 2650);

// iot / tech / events
AddDoc(bIot, "mqtt", "MQTT 客户端使用指南",
    "NewLife.MQTT 是物联网标准 MQTT 协议实现,支持客户端与服务器端。",
    "<h2 id=\"s1\">MQTT 简介</h2><p>物联网标准协议,基于发布订阅模型。</p>",
    DateTime.Parse("2026-03-15"), 1980);

AddDoc(bTech, "csharp14", "C# 14 新特性速览",
    "集合表达式、空条件赋值、扩展访问修饰符等 C# 14 新特性实践。",
    "<h2 id=\"s1\">集合表达式</h2><pre><code>List&lt;String&gt; list = [];</code></pre>",
    DateTime.Parse("2026-02-05"), 4200);

AddDoc(bEvents, "meetup2026", "2026 年技术沙龙回顾",
    "新生命团队 2026 年技术沙龙活动回顾:开源生态、物联网实践与 AI 赋能。",
    "<h2 id=\"s1\">活动回顾</h2><p>本次沙龙共 200+ 开发者参与,聚焦 .NET 生态与物联网实践。</p>",
    DateTime.Parse("2026-01-12"), 880);

// blood 产品宣传页(Format=product,Sync=false,本地维护)
// 完整产品介绍 HTML 独立维护在 Tools/SeedData/Products/*.html(便于预览 / 复用 / 后台粘贴)
AddDoc(bBlood, "stardust", "星尘 Stardust - 分布式可观测平台",
    "轻量级可观测 APM、服务守护、配置中心三位一体,为 .NET 分布式服务提供开箱即用的全链路监控与治理能力。",
    ReadProductHtml("stardust"),
    DateTime.Parse("2026-04-23"), 12800, false, "product");

AddDoc(bBlood, "antjob", "蚂蚁调度 AntJob - 分布式任务调度",
    "分布式、灵活、部署便捷,多模式多样化适配各类数据。日均 10 亿级数据实时计算,延迟小于 1 分钟。",
    ReadProductHtml("antjob"),
    DateTime.Parse("2026-04-23"), 9860, false, "product");

AddDoc(bBlood, "newlife", "新生命团队",
    "新生命团队(NewLife)成立于2002年,是新时代物联网行业解决方案提供者,致力于提供软硬件应用方案咨询、系统架构规划与开发服务。",
    "<h2 id=\"s1\">团队简介</h2><p>新生命团队(NewLife)成立于 2002 年,是新时代物联网行业解决方案提供者。</p><h2 id=\"s2\">生态组件</h2><ul><li>核心组件 NewLife.Core</li><li>大数据中间件 NewLife.XCode</li><li>魔方快速开发平台 NewLife.Cube</li><li>分布式平台 Stardust</li></ul>",
    DateTime.Parse("2023-04-23"), 37289);

XTrace.WriteLine("Document 文档 OK");
#endregion

XTrace.WriteLine("测试数据初始化完成!");

/// <summary>读取产品宣传页 HTML 文件。兼容从 Bin/Web 运行(复制产物)与源码目录两种方式</summary>
/// <param name="name">文件名(不含扩展名),如 stardust</param>
/// <returns>HTML 内容</returns>
String ReadProductHtml(String name)
{
    var file = name + ".html";
    var paths = new[]
    {
        // 构建产物目录:Bin/Web/Products(csproj CopyToOutputDirectory)
        Path.Combine(AppContext.BaseDirectory, "Products", file),
        // 源码目录:Tools/SeedData/Products(从仓库根 dotnet run 时)
        Path.Combine("Tools", "SeedData", "Products", file),
        Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "Tools", "SeedData", "Products", file),
    };
    foreach (var path in paths)
    {
        if (File.Exists(path)) return File.ReadAllText(path);
    }
    throw new FileNotFoundException($"未找到产品页 HTML:{file}", file);
}