<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>FuKun.Net –</title><link>https://fukun.net/blog/</link><description>Recent content on FuKun.Net</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Thu, 21 May 2026 15:00:00 +0800</lastBuildDate><atom:link href="https://fukun.net/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>域名折腾记录</title><link>https://fukun.net/blog/site-launch-guide/</link><pubDate>Thu, 21 May 2026 15:00:00 +0800</pubDate><guid>https://fukun.net/blog/site-launch-guide/</guid><description>
&lt;p&gt;把个人网站从 &lt;code&gt;http://IP&lt;/code&gt; 变成一个正经的 &lt;code&gt;https://域名&lt;/code&gt;，中间要走的路比想象中多。这篇文章记录我的实际操作过程，供有同样需求的朋友参考。&lt;/p&gt;
&lt;h2&gt;前提&lt;span class="hx:absolute hx:-mt-20" id="前提"&gt;&lt;/span&gt;
&lt;a href="#%e5%89%8d%e6%8f%90" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;服务器：腾讯云 Lighthouse，Ubuntu 24.04，4 核 4G&lt;/li&gt;
&lt;li&gt;网站：Hugo 静态博客，Nginx 提供服务&lt;/li&gt;
&lt;li&gt;域名注册商：腾讯云&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;一、域名申请&lt;span class="hx:absolute hx:-mt-20" id="一域名申请"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%80%e5%9f%9f%e5%90%8d%e7%94%b3%e8%af%b7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;控制台路径&lt;/strong&gt;：腾讯云控制台 → 产品 → 域名注册 → 域名注册&lt;/p&gt;
&lt;p&gt;在腾讯云域名注册页面搜索想要的域名，&lt;code&gt;.com&lt;/code&gt; 早就被抢光了，&lt;code&gt;.net&lt;/code&gt; 还有一些。我选了 &lt;code&gt;fukun.net&lt;/code&gt;，首年 90 元。&lt;/p&gt;
&lt;p&gt;购买后进入 &lt;strong&gt;控制台 → 产品 → 域名注册 → 我的域名&lt;/strong&gt;，找到刚买的域名，点击「实名认证」。上传身份证正反面，审核大约半小时到两小时。通过后域名状态变为「正常」。&lt;/p&gt;
&lt;p&gt;回到我的域名页面，点击域名进入 &lt;strong&gt;域名管理 → DNS 解析 → 添加记录&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;主机记录&lt;/th&gt;
&lt;th&gt;记录类型&lt;/th&gt;
&lt;th&gt;记录值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;62.234.90.54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;www&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;62.234.90.54&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;TTL 默认 600（即 10 分钟）即可。关于 TTL 的设置经验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;日常使用&lt;/strong&gt;：600 足够。DNS 修改后最迟 10 分钟全球生效，实际通常更快。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迁移前夕&lt;/strong&gt;：如果计划更换服务器 IP，提前 24 小时把 TTL 降到 60–120，这样迁移时 DNS 切换更快。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要设太短&lt;/strong&gt;：TTL 过短（如 10 秒）会增加 DNS 查询量，对个人站点没必要。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要设太长&lt;/strong&gt;：TTL 过长（如 86400）会导致修改后一天才能生效，出问题时无法快速切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;生效很快，几分钟后 &lt;code&gt;ping fukun.net&lt;/code&gt; 就能看到正确 IP。&lt;/p&gt;
&lt;h2&gt;二、工信部 ICP 备案&lt;span class="hx:absolute hx:-mt-20" id="二工信部-icp-备案"&gt;&lt;/span&gt;
&lt;a href="#%e4%ba%8c%e5%b7%a5%e4%bf%a1%e9%83%a8-icp-%e5%a4%87%e6%a1%88" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这是整个流程里最耗时的环节。根据《互联网信息服务管理办法》，中国大陆境内提供互联网信息服务的网站必须取得 ICP 备案号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;控制台路径&lt;/strong&gt;：腾讯云控制台 → 产品 → 网站备案 → 开始备案&lt;/p&gt;
&lt;h3&gt;操作流程&lt;span class="hx:absolute hx:-mt-20" id="操作流程"&gt;&lt;/span&gt;
&lt;a href="#%e6%93%8d%e4%bd%9c%e6%b5%81%e7%a8%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;进入备案系统，选择「首次备案」&lt;/li&gt;
&lt;li&gt;填写主体信息（个人姓名、身份证号、详细通信地址）&lt;/li&gt;
&lt;li&gt;填写网站信息：
&lt;ul&gt;
&lt;li&gt;网站名称：注意不能包含「中国」「中华」等字样，个人备案也不能用太过商业化的名称（我填了「FuKun 技术笔记」）&lt;/li&gt;
&lt;li&gt;网站域名：&lt;code&gt;fukun.net&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;网站简介：如实描述，不要写「论坛」「商城」等需要前置审批的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;上传证件照片（身份证正反面、手持身份证照）&lt;/li&gt;
&lt;li&gt;进行人脸识别验证（微信扫码完成）&lt;/li&gt;
&lt;li&gt;提交初审&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;时间线&lt;span class="hx:absolute hx:-mt-20" id="时间线"&gt;&lt;/span&gt;
&lt;a href="#%e6%97%b6%e9%97%b4%e7%ba%bf" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;阶段&lt;/th&gt;
&lt;th&gt;耗时&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;腾讯云初审&lt;/td&gt;
&lt;td&gt;1 个工作日&lt;/td&gt;
&lt;td&gt;审核材料完整性，不合格会退回修改&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;提交管局&lt;/td&gt;
&lt;td&gt;3 个工作日&lt;/td&gt;
&lt;td&gt;初审通过后提交至通信管理局&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;工信部短信核验&lt;/td&gt;
&lt;td&gt;即时&lt;/td&gt;
&lt;td&gt;收到短信后 24 小时内点击链接确认&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通信管理局审核&lt;/td&gt;
&lt;td&gt;10–15 个工作日&lt;/td&gt;
&lt;td&gt;各地管局速度不同，广东大约 12 天&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我从提交到拿到备案号大约用了 &lt;strong&gt;12 个工作日&lt;/strong&gt;。通过后会在 &lt;strong&gt;控制台 → 网站备案 → 我的备案&lt;/strong&gt; 看到备案号和电子证书。&lt;/p&gt;
&lt;h3&gt;关键注意事项&lt;span class="hx:absolute hx:-mt-20" id="关键注意事项"&gt;&lt;/span&gt;
&lt;a href="#%e5%85%b3%e9%94%ae%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;备案期间网站&lt;strong&gt;必须关闭&lt;/strong&gt;（Nginx 先别配域名，或者返回 403）&lt;/li&gt;
&lt;li&gt;个人备案不得涉及企业、商品、新闻等内容&lt;/li&gt;
&lt;li&gt;备案号需要在网站底部展示，链接到 &lt;code&gt;beian.miit.gov.cn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;备案通过后 30 天内需完成公安备案&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;三、SSL 证书申请&lt;span class="hx:absolute hx:-mt-20" id="三ssl-证书申请"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%89ssl-%e8%af%81%e4%b9%a6%e7%94%b3%e8%af%b7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;备案通过后域名可以解析了，此时网站还是 HTTP。下一步申请 SSL 证书开启 HTTPS。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;控制台路径&lt;/strong&gt;：腾讯云控制台 → 产品 → SSL 证书 → 我的证书 → 申请免费证书&lt;/p&gt;
&lt;p&gt;我使用的是腾讯云「SSL 单域名证书（一年期）」，TrustAsia DV 品牌，RSA 2048 位加密，SHA256 签名算法。证书覆盖主域名 &lt;code&gt;fukun.net&lt;/code&gt; 和 &lt;code&gt;www.fukun.net&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;证书链：&lt;code&gt;fukun.net&lt;/code&gt; → TrustAsia DV TLS RSA CA 2025 → DigiCert Global Root G2。&lt;/p&gt;
&lt;h3&gt;操作步骤&lt;span class="hx:absolute hx:-mt-20" id="操作步骤"&gt;&lt;/span&gt;
&lt;a href="#%e6%93%8d%e4%bd%9c%e6%ad%a5%e9%aa%a4" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;在 SSL 证书控制台点击「申请免费证书」（或购买对应类型的证书，¥64.6/年）&lt;/li&gt;
&lt;li&gt;填写申请信息：
&lt;ul&gt;
&lt;li&gt;证书绑定域名：&lt;code&gt;fukun.net&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;选择「自动添加 DNS 验证记录」— 腾讯云会自动添加一条 TXT 记录&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;提交申请，等待验证通过（通常 1–5 分钟）&lt;/li&gt;
&lt;li&gt;验证通过后状态变为「已签发」，点击「下载」&lt;/li&gt;
&lt;li&gt;选择「Nginx」格式下载，得到一个 zip 包&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;解压后得到四个文件：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文件&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fukun.net.csr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;证书请求文件，申请时自动生成，部署不需要&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fukun.net.key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;私钥，Nginx ssl_certificate_key 使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fukun.net_bundle.crt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CRT 格式证书，含中间证书，Nginx 使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fukun.net_bundle.pem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PEM 格式证书，与 CRT 同内容，备用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;四、Nginx SSL 配置&lt;span class="hx:absolute hx:-mt-20" id="四nginx-ssl-配置"&gt;&lt;/span&gt;
&lt;a href="#%e5%9b%9bnginx-ssl-%e9%85%8d%e7%bd%ae" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;上传证书到服务器&lt;span class="hx:absolute hx:-mt-20" id="上传证书到服务器"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%8a%e4%bc%a0%e8%af%81%e4%b9%a6%e5%88%b0%e6%9c%8d%e5%8a%a1%e5%99%a8" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 将证书文件传到服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scp fukun.net.key fukun.net_bundle.crt ubuntu@62.234.90.54:~/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SSH 登录服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh ubuntu@62.234.90.54
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 移动到 Nginx SSL 目录并设置权限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mv ~/fukun.net.key ~/fukun.net_bundle.crt /etc/nginx/ssl/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown root:root /etc/nginx/ssl/fukun.net*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;600&lt;/span&gt; /etc/nginx/ssl/fukun.net.key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Nginx 站点配置&lt;span class="hx:absolute hx:-mt-20" id="nginx-站点配置"&gt;&lt;/span&gt;
&lt;a href="#nginx-%e7%ab%99%e7%82%b9%e9%85%8d%e7%bd%ae" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;编辑 &lt;code&gt;/etc/nginx/sites-available/hugo-blog&lt;/code&gt;：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HTTP → HTTPS 重定向
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;fukun.net&lt;/span&gt; &lt;span class="s"&gt;www.fukun.net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HTTPS 站点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;fukun.net&lt;/span&gt; &lt;span class="s"&gt;www.fukun.net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/ssl/fukun.net_bundle.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/ssl/fukun.net.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt; &lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_session_cache&lt;/span&gt; &lt;span class="s"&gt;shared:SSL:10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_session_timeout&lt;/span&gt; &lt;span class="mi"&gt;10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/home/hugo/hugo-blog/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 静态资源缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;\.(css|js|jpg|jpeg|png|gif|ico|svg|webp)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;30d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;public,&lt;/span&gt; &lt;span class="s"&gt;immutable&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 启用站点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ln -sf /etc/nginx/sites-available/hugo-blog /etc/nginx/sites-enabled/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 测试配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 重载 Nginx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;五、备案号悬挂&lt;span class="hx:absolute hx:-mt-20" id="五备案号悬挂"&gt;&lt;/span&gt;
&lt;a href="#%e4%ba%94%e5%a4%87%e6%a1%88%e5%8f%b7%e6%82%ac%e6%8c%82" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;工信部要求备案号须挂在网站首页底部。在 Hugo 的 &lt;code&gt;hugo.yaml&lt;/code&gt; 配置：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;icp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;粤ICP备2026061516号&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;在布局文件 &lt;code&gt;layouts/_partials/footer.html&lt;/code&gt; 中渲染：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Site.Params.footer.icp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hx:mt-2 hx:text-xs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://beian.miit.gov.cn/&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;_blank&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;noopener noreferrer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;六、公安部网站备案&lt;span class="hx:absolute hx:-mt-20" id="六公安部网站备案"&gt;&lt;/span&gt;
&lt;a href="#%e5%85%ad%e5%85%ac%e5%ae%89%e9%83%a8%e7%bd%91%e7%ab%99%e5%a4%87%e6%a1%88" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;ICP 备案通过后 30 天内，还需进行公安机关互联网站备案。注意需要先完成主体备案，再完成网站备案，两步都要做。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;网站&lt;/strong&gt;：www.beian.gov.cn → 全国公安机关互联网站安全服务平台&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：主体备案&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;注册账号并登录&lt;/li&gt;
&lt;li&gt;进入「办事大厅」→「开办主体管理」→「新增主体」&lt;/li&gt;
&lt;li&gt;填写主体信息（个人姓名、身份证号、地址、联系方式等）&lt;/li&gt;
&lt;li&gt;提交审核，通过后可进行网站备案&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;第二步：网站备案&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进入「办事大厅」→「新办网站申请」&lt;/li&gt;
&lt;li&gt;填写网站信息：网站名称、域名、IP 地址、服务器所在地&lt;/li&gt;
&lt;li&gt;选择网络接入服务商：腾讯云&lt;/li&gt;
&lt;li&gt;提交审核（约 7–10 个工作日）&lt;/li&gt;
&lt;li&gt;审核通过后获取公安备案号，格式为「粤公网安备 XXXXXXX 号」&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;公安备案号也需要悬挂在网站上（通常放在 ICP 备案号旁边）。&lt;/p&gt;
&lt;h2&gt;七、新增二级域名&lt;span class="hx:absolute hx:-mt-20" id="七新增二级域名"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%83%e6%96%b0%e5%a2%9e%e4%ba%8c%e7%ba%a7%e5%9f%9f%e5%90%8d" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网站上线后如需新增二级域名（如 &lt;code&gt;gallery.fukun.net&lt;/code&gt;），二级域名与主域名共用同一个 ICP 备案号，公安备案同理，不需要重复办理。只需要做好DNS解析、证书申请、Nginx配置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DNS 解析&lt;/strong&gt;：在域名管理 → DNS 解析中新增 A 记录，主机记录填 &lt;code&gt;gallery&lt;/code&gt;，指向同一 IP。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSL 证书&lt;/strong&gt;：单域名证书覆盖不到新二级域名，需要为新二级域名单独申请一张（流程同第三章，约 5 分钟）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nginx 配置&lt;/strong&gt;：新增站点文件 &lt;code&gt;/etc/nginx/sites-available/gallery&lt;/code&gt;：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;gallery.fukun.net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/ssl/gallery_bundle.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/ssl/gallery.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt; &lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/home/hugo/blog-site/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HTTP 跳转 HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;gallery.fukun.net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;启用并重载：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ln -sf /etc/nginx/sites-available/gallery /etc/nginx/sites-enabled/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;事项&lt;/th&gt;
&lt;th&gt;耗时&lt;/th&gt;
&lt;th&gt;费用&lt;/th&gt;
&lt;th&gt;控制台路径&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;云服务器&lt;/td&gt;
&lt;td&gt;1 天&lt;/td&gt;
&lt;td&gt;¥99/月&lt;/td&gt;
&lt;td&gt;控制台 → Lighthouse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;域名注册&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;¥90/年&lt;/td&gt;
&lt;td&gt;控制台 → 域名注册&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;实名认证&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;控制台 → 域名注册 → 我的域名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS 解析&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;域名管理 → DNS 解析&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ICP 备案&lt;/td&gt;
&lt;td&gt;10–15 工作日&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;控制台 → 网站备案&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL 证书&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;¥64.6/年&lt;/td&gt;
&lt;td&gt;控制台 → SSL 证书 → 我的证书&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN 加速&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;¥14/年&lt;/td&gt;
&lt;td&gt;控制台 → CDN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;云硬盘扩容&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;¥70&lt;/td&gt;
&lt;td&gt;控制台 → Lighthouse → 云硬盘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx 配置&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;公安备案&lt;/td&gt;
&lt;td&gt;7–10 工作日&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;beian.gov.cn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;合计&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;约 25–33 天&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;¥337.6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Mermaid 图表语法示例</title><link>https://fukun.net/blog/mermaid-test/</link><pubDate>Tue, 19 May 2026 10:00:00 +0800</pubDate><guid>https://fukun.net/blog/mermaid-test/</guid><description>
&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://mermaid.js.org/syntax/examples.html"target="_blank" rel="noopener"&gt;Mermaid Examples&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Examples&lt;/h1&gt;&lt;p&gt;This page contains a collection of examples of diagrams and charts that can be created through mermaid and its myriad applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you wish to learn how to support mermaid on your webpage, read the &lt;a href="../config/usage.md?id=usage"&gt;Beginner&amp;rsquo;s Guide&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you wish to learn about mermaid&amp;rsquo;s syntax, Read the &lt;a href="../syntax/flowchart.md?id=flowcharts-basic-syntax"&gt;Diagram Syntax&lt;/a&gt; section.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Basic Pie Chart&lt;span class="hx:absolute hx:-mt-20" id="basic-pie-chart"&gt;&lt;/span&gt;
&lt;a href="#basic-pie-chart" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
pie title NETFLIX
&amp;#34;Time spent looking for movie&amp;#34; : 90
&amp;#34;Time spent watching it&amp;#34; : 10
&lt;/pre&gt;
&lt;/div&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
pie title What Voldemort doesn&amp;#39;t have?
&amp;#34;FRIENDS&amp;#34; : 2
&amp;#34;FAMILY&amp;#34; : 3
&amp;#34;NOSE&amp;#34; : 45
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;Basic sequence diagram&lt;span class="hx:absolute hx:-mt-20" id="basic-sequence-diagram"&gt;&lt;/span&gt;
&lt;a href="#basic-sequence-diagram" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
sequenceDiagram
Alice -&amp;gt;&amp;gt; Bob: Hello Bob, how are you?
Bob--&amp;gt;&amp;gt;John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: Bob thinks a long&amp;lt;br/&amp;gt;long time, so long&amp;lt;br/&amp;gt;that the text does&amp;lt;br/&amp;gt;not fit on a row.
Bob--&amp;gt;Alice: Checking with John...
Alice-&amp;gt;John: Yes... John, how are you?
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;Basic flowchart&lt;span class="hx:absolute hx:-mt-20" id="basic-flowchart"&gt;&lt;/span&gt;
&lt;a href="#basic-flowchart" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
graph LR
A[Square Rect] -- Link text --&amp;gt; B((Circle))
A --&amp;gt; C(Round Rect)
B --&amp;gt; D{Rhombus}
C --&amp;gt; D
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;SequenceDiagram: Loops, alt and opt&lt;span class="hx:absolute hx:-mt-20" id="sequencediagram-loops-alt-and-opt"&gt;&lt;/span&gt;
&lt;a href="#sequencediagram-loops-alt-and-opt" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
sequenceDiagram
loop Daily query
Alice-&amp;gt;&amp;gt;Bob: Hello Bob, how are you?
alt is sick
Bob-&amp;gt;&amp;gt;Alice: Not so good :(
else is well
Bob-&amp;gt;&amp;gt;Alice: Feeling fresh like a daisy
end
opt Extra response
Bob-&amp;gt;&amp;gt;Alice: Thanks for asking
end
end
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;SequenceDiagram: Message to self in loop&lt;span class="hx:absolute hx:-mt-20" id="sequencediagram-message-to-self-in-loop"&gt;&lt;/span&gt;
&lt;a href="#sequencediagram-message-to-self-in-loop" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
sequenceDiagram
participant Alice
participant Bob
Alice-&amp;gt;&amp;gt;John: Hello John, how are you?
loop HealthCheck
John-&amp;gt;&amp;gt;John: Fight against hypochondria
end
Note right of John: Rational thoughts&amp;lt;br/&amp;gt;prevail...
John--&amp;gt;&amp;gt;Alice: Great!
John-&amp;gt;&amp;gt;Bob: How about you?
Bob--&amp;gt;&amp;gt;John: Jolly good!
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;Sequence Diagram: Blogging app service communication&lt;span class="hx:absolute hx:-mt-20" id="sequence-diagram-blogging-app-service-communication"&gt;&lt;/span&gt;
&lt;a href="#sequence-diagram-blogging-app-service-communication" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
sequenceDiagram
participant web as Web Browser
participant blog as Blog Service
participant account as Account Service
participant mail as Mail Service
participant db as Storage
Note over web,db: The user must be logged in to submit blog posts
web-&amp;gt;&amp;gt;+account: Logs in using credentials
account-&amp;gt;&amp;gt;db: Query stored accounts
db-&amp;gt;&amp;gt;account: Respond with query result
alt Credentials not found
account-&amp;gt;&amp;gt;web: Invalid credentials
else Credentials found
account-&amp;gt;&amp;gt;-web: Successfully logged in
Note over web,db: When the user is authenticated, they can now submit new posts
web-&amp;gt;&amp;gt;+blog: Submit new post
blog-&amp;gt;&amp;gt;db: Store post data
par Notifications
blog--)mail: Send mail to blog subscribers
blog--)db: Store in-site notifications
and Response
blog--&amp;gt;&amp;gt;-web: Successfully posted
end
end
&lt;/pre&gt;
&lt;/div&gt;&lt;h2&gt;A commit flow diagram.&lt;span class="hx:absolute hx:-mt-20" id="a-commit-flow-diagram"&gt;&lt;/span&gt;
&lt;a href="#a-commit-flow-diagram" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
gitGraph:
commit &amp;#34;Ashish&amp;#34;
branch newbranch
checkout newbranch
commit id:&amp;#34;1111&amp;#34;
commit tag:&amp;#34;test&amp;#34;
checkout main
commit type: HIGHLIGHT
commit
merge newbranch
commit
branch b2
commit
&lt;/pre&gt;
&lt;/div&gt;&lt;!--- cspell:ignore Ashish newbranch ---&gt;</description></item><item><title>用宝可梦解释 Prolog 基础</title><link>https://fukun.net/blog/prolog-pokemon/</link><pubDate>Mon, 18 May 2026 13:30:00 +0800</pubDate><guid>https://fukun.net/blog/prolog-pokemon/</guid><description>
&lt;blockquote&gt;
&lt;p&gt;原文作者：Alexander Petros&lt;br&gt;
原文链接：&lt;a href="https://unplannedobsolescence.com/blog/prolog-basics-pokemon/"target="_blank" rel="noopener"&gt;Prolog Basics Explained with Pokémon&lt;/a&gt;&lt;br&gt;
本文已获授权翻译，有删节和注释。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;启发这篇文章的项目有点傻——我要详细描述一个儿童电子游戏的机制——但正是这个特定问题最终让我真正理解了 Prolog。这是自从读了 Bruce Tate 的《七周七语言》以来一直在追寻的顿悟。&lt;/p&gt;
&lt;p&gt;对于某些类型的关系，逻辑编程是我用过的最简洁、最具表现力的编程系统。要理解为什么，让我们来聊聊宝可梦。&lt;/p&gt;
&lt;h2&gt;宝可梦基础&lt;span class="hx:absolute hx:-mt-20" id="宝可梦基础"&gt;&lt;/span&gt;
&lt;a href="#%e5%ae%9d%e5%8f%af%e6%a2%a6%e5%9f%ba%e7%a1%80" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;宝可梦是一个电子游戏系列，设定在一个人类与各种色彩缤纷的动物角色共存的世界。&amp;ldquo;Pokémon&amp;rdquo; 既是系列名称，也是这些动物角色的统称，每个角色都有自己的物种名称。从妙蛙种子（#1）到桃歹郎（#1025），共有超过一千种不同的宝可梦物种。&lt;/p&gt;
&lt;div style="display:flex;gap:8px;justify-content:center;margin:1rem 0"&gt;&lt;img src="https://fukun.net/images/pokemon/pikachu.png" alt="皮卡丘" style="width:32%"&gt;&lt;img src="https://fukun.net/images/pokemon/archeops.png" alt="始祖大鸟" style="width:32%"&gt;&lt;img src="https://fukun.net/images/pokemon/dipplin.png" alt="裹蜜虫" style="width:32%"&gt;&lt;/div&gt;
&lt;figcaption class="post-caption"&gt;受欢迎的宝可梦包括（从左到右）：皮卡丘（#25）、始祖大鸟（#567）和裹蜜虫（#1101）。&lt;/figcaption&gt;
&lt;p&gt;现在有各种各样的宝可梦游戏，但主系列始终围绕着捕捉和对战。在对战中，你的六只宝可梦队伍与另一支队伍交锋。每只宝可梦配备四个招式，通常用来对对手造成伤害。你需要将对方所有宝可梦的 HP 减到零，同时防止对方先对你做同样的事。&lt;/p&gt;
&lt;p&gt;每只宝可梦都有独特的特性影响对战表现：基础能力值、可学的招式、特性和属性组合。这里的庞大组合数量正是试图用软件来追踪这些信息的动机。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/pokemon/scizor-stats.png" alt="巨钳螳螂的能力值（来自 Smogon）" loading="lazy" /&gt;&lt;/p&gt;
&lt;figcaption class="post-caption"&gt;巨钳螳螂是虫/钢属性，攻击力高但速度低（数据来自 Smogon）。&lt;/figcaption&gt;
&lt;p&gt;&lt;strong&gt;速度&lt;/strong&gt;决定哪个招式先出手；&lt;strong&gt;攻击和特攻&lt;/strong&gt;分别影响物理招式和特殊招式的伤害；&lt;strong&gt;防御和特防&lt;/strong&gt;影响受到的伤害。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;尤其重要。招式有属性（如火或岩石），宝可梦可以拥有最多两种属性。如果招式属性对对方宝可梦效果绝佳，造成双倍伤害；效果不佳则只造成一半伤害。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/pokemon/surf.png" alt="冲浪对月石效果绝佳" loading="lazy" /&gt;&lt;/p&gt;
&lt;figcaption class="post-caption"&gt;月石是岩石/超能力属性。岩石弱水，超能力对水中性，所以冲浪会造成 2 倍伤害。&lt;/figcaption&gt;
&lt;p&gt;举个直观的例子：火属性的喷射火焰对草属性宝可梦造成 2 倍伤害（草弱火），但水属性的冲浪只造成 ½ 伤害（草抗水）。&lt;/p&gt;
&lt;p&gt;属性修正可以叠加。巨钳螳螂是虫/钢属性，虫和钢都弱火，所以火属性招式对它造成 4 倍伤害。电弱水，但地面免疫电——如果你对水/地面的巨沼怪使用电属性招式，伤害为零，因为 0×2 还是 0。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/pokemon/type-chart.svg" alt="宝可梦属性相克表（来自 Wikimedia）" loading="lazy" /&gt;&lt;/p&gt;
&lt;figcaption class="post-caption"&gt;宝可梦属性相克表。&lt;/figcaption&gt;
&lt;p&gt;这些基本上就是我 8 岁时理解的宝可梦机制。点击招式造成伤害，尽量选择属性相克有利的招式。这些游戏是为儿童设计的，表面上看并不难。&lt;/p&gt;
&lt;h2&gt;Prolog 基础&lt;span class="hx:absolute hx:-mt-20" id="prolog-基础"&gt;&lt;/span&gt;
&lt;a href="#prolog-%e5%9f%ba%e7%a1%80" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在解释宝可梦机制底层有多复杂之前，先解释逻辑编程的工作原理。宝可梦非常适合逻辑编程，因为宝可梦对战本质上是一个极其精密的规则引擎。&lt;/p&gt;
&lt;p&gt;首先创建一个包含事实的文件：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;bulbasaur&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;ivysaur&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;venusaur&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;charmander&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;charmeleon&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;charizard&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;squirtle&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;wartortle&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;blastoise&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;在 Prolog 中，我们声明&amp;quot;谓词&amp;quot;（Predicate）。谓词定义关系：&lt;code&gt;bulbasaur&lt;/code&gt; 是一个 &lt;code&gt;pokemon&lt;/code&gt;，&lt;code&gt;charmander&lt;/code&gt; 是一个 &lt;code&gt;pokemon&lt;/code&gt;，以此类推。我们称这个谓词为 &lt;code&gt;pokemon/1&lt;/code&gt;，因为它有一个参数。&lt;/p&gt;
&lt;p&gt;这些事实被加载到一个交互式提示符——&amp;ldquo;顶层&amp;rdquo;（top-level）中。你输入一个语句到提示符，Prolog 试图找出所有使该语句为真的方式：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- pokemon(squirtle).
true.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;不是所有东西都是宝可梦。&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- pokemon(alex).
false.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;接着添加宝可梦的属性，作为谓词 &lt;code&gt;type/2&lt;/code&gt;：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;bulbasaur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;bulbasaur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;poison&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;ivysaur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;ivysaur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;poison&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;charmander&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;squirtle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;blastoise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;有些宝可梦只有一种属性，有些有两种。后一种情况用两个 &lt;code&gt;type&lt;/code&gt; 事实建模。妙蛙种子是草属性，也是毒属性——两者都为真。&lt;/p&gt;
&lt;p&gt;交互式查询：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- type(squirtle, water).
true.
?- type(squirtle, grass).
false.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Prolog 中首字母大写的名字是&lt;strong&gt;变量&lt;/strong&gt;。Prolog 尝试将谓词与变量的所有可能匹配进行&amp;quot;合一&amp;quot;：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- type(squirtle, Type).
Type = water.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;对于有两种属性的宝可梦，谓词会合一两次：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- type(venusaur, Type).
Type = grass
; Type = poison.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;分号 &lt;code&gt;;&lt;/code&gt; 表示&amp;quot;或&amp;quot;。任何参数都可以是变量，这意味着我们可以从任意方向提问。所有草属性宝可梦有哪些？只需把第一个参数设为变量：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- type(Pokemon, grass).
Pokemon = bulbasaur
; Pokemon = ivysaur
; Pokemon = venusaur
; ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;逗号用于列出多个谓词——Prolog 会合一变量使得所有谓词都为真。列出所有水/冰属性的宝可梦：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- type(Pokemon, water), type(Pokemon, ice).
Pokemon = dewgong
; Pokemon = cloyster
; Pokemon = lapras
; Pokemon = ironbundle
; false.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Iron Bundle 是一只水/冰属性宝可梦，特攻很高。具体多高？&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- pokemon_spa(ironbundle, SpA).
SpA = 124.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;特攻这么高，我们想利用强力的特殊招式。Iron Bundle 会哪些特殊招式？&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- learns(ironbundle, Move), move_category(Move, special).
Move = blizzard
; Move = freezedry
; Move = hydropump
; Move = icebeam
; Move = icywind
; ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Freeze-Dry 是一个特别好的特殊招式。找出所有特攻大于 120、会 Freeze-Dry 的冰属性宝可梦：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- pokemon_spa(Pokemon, SpA), SpA #&amp;gt; 120, learns(Pokemon, freezedry), type(Pokemon, ice).
Pokemon = glaceon, SpA = 130
; Pokemon = kyurem, SpA = 130
; Pokemon = kyuremwhite, SpA = 170
; Pokemon = ironbundle, SpA = 124.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;最后一个概念：&lt;strong&gt;规则&lt;/strong&gt;（Rules）。规则有头部和主体，如果主体为真，则规则合一：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;damaging_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;move_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;physical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;move_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;special&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- damaging_move(tackle).
true.
?- damaging_move(rest).
false.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;SQL 对比&lt;span class="hx:absolute hx:-mt-20" id="sql-对比"&gt;&lt;/span&gt;
&lt;a href="#sql-%e5%af%b9%e6%af%94" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;到目前为止展示的内容在逻辑上并不复杂——只是关于各种事实的&amp;quot;与&amp;quot;和&amp;quot;或&amp;quot;语句。不过请花一点时间体会一下，查询这个数据库比 SQL 舒服多少。&lt;/p&gt;
&lt;p&gt;我会这样设置 SQL 表：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;special_attack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon_moves&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;然后查询特攻 &amp;gt; 120、会 Freeze-Dry 的冰属性宝可梦：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;special_attack&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;special_attack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon_moves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;freezedry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pokemon_types&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ice&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;对比等效的 Prolog 查询：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- pokemon_spa(Pokemon, SpA), SpA #&amp;gt; 120, learns(Pokemon, freezedry), type(Pokemon, ice).&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;我不是在贬低 SQL——我很喜欢 SQL——但 Prolog 版本简单和灵活得令人惊叹。如果我们继续添加更多条件，SQL 查询会变得难以管理，而 Prolog 查询仍然易于阅读和编辑。&lt;/p&gt;
&lt;h2&gt;升级&lt;span class="hx:absolute hx:-mt-20" id="升级"&gt;&lt;/span&gt;
&lt;a href="#%e5%8d%87%e7%ba%a7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;宝可梦对战拥有数量惊人的机制，以复杂且概率性的方式相互影响。我尚未提及的部分机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;某些招式有一定概率落空&lt;/li&gt;
&lt;li&gt;某些招式会提升或降低能力值&lt;/li&gt;
&lt;li&gt;宝可梦可以携带具有各种效果的物品&lt;/li&gt;
&lt;li&gt;伤害计算不是恒定的，而是呈正态分布&lt;/li&gt;
&lt;li&gt;宝可梦可以被冰冻、灼伤、麻痹、中毒或陷入睡眠&lt;/li&gt;
&lt;li&gt;各种场地效果（天气、戏法空间等）会改变招式伤害和出手顺序&lt;/li&gt;
&lt;li&gt;每只宝可梦都有一个特性（如漂浮免疫地面招式、降雨改变天气、强行增伤 1.3 倍）&lt;/li&gt;
&lt;li&gt;玩家分配努力值提升选定能力值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你想为这个游戏构建软件，挑战在于在所有复杂性的建模过程中不疯掉。Prolog 在这方面出奇地擅长，主要有两个原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查询模型擅长描述临时组合&lt;/li&gt;
&lt;li&gt;数据模型非常适合以一致的方式分层叠加规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了说明这一点，以下是我为宝可梦选秀联赛实现优先度招式的过程。&lt;/p&gt;
&lt;p&gt;宝可梦选秀大致就是字面意思。宝可梦根据实力被赋予分值，每个玩家获得一定分数，轮流选直到用完。你的队伍最终会有大约 8-11 只宝可梦，每周你和联赛中的另一个人正面交锋。&lt;/p&gt;
&lt;p&gt;我定义的队伍：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;meowscarada&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;weezinggalar&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;swampertmega&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;latios&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;volcarona&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;tornadus&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;politoed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;archaludon&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;beartic&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;alex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;dusclops&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;我的宝可梦里有哪些会 Freeze-Dry？&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- alex(Pokemon), learns(Pokemon, freezedry).
false.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;一个都没有。唉。&lt;/p&gt;
&lt;p&gt;非常重要的一类招式为&lt;strong&gt;优先度招式&lt;/strong&gt;。大多数招式的优先度为零：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- move_priority(Move, P).
Move = absorb, P = 0
; Move = accelerock, P = 1
; Move = acid, P = 0
; ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Accelerock 的优先度为 1。使用它的宝可梦会先于任何使用优先度 0 招式的宝可梦行动，即使后者速度更高。&lt;/p&gt;
&lt;p&gt;我定义一个 &lt;code&gt;learns_priority/3&lt;/code&gt; 谓词：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;learns_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;learns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;move_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;P&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;P&lt;/span&gt; &lt;span class="s"&gt;#&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&amp;ldquo;我的队伍学会了哪些优先度招式&amp;rdquo;——返回了大量答案，但很多是双打专用招式。过滤掉双打招式和保护招式：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;learns_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;learns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;\+&lt;/span&gt; &lt;span class="nf"&gt;doubles_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;\+&lt;/span&gt; &lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Move&lt;/span&gt; &lt;span class="s"&gt;\=&lt;/span&gt; &lt;span class="s"&gt;bide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;move_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt; &lt;span class="s"&gt;#&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;doubles_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;helpinghand&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;doubles_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;allyswitch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;doubles_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;ragepowder&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;endure&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;magiccoat&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;搞定！结果精简为真正有用的优先度招式：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- alex(Pokemon), learns_priority(Pokemon, Move, Priority).
Pokemon = meowscarada, Move = quickattack, Priority = 1
; Pokemon = meowscarada, Move = suckerpunch, Priority = 1
; Pokemon = beartic, Move = aquajet, Priority = 1
; Pokemon = dusclops, Move = shadowsneak, Priority = 1
; Pokemon = dusclops, Move = suckerpunch, Priority = 1.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;优先度招式的缺乏实际上是我的队伍的一大弱点。&lt;/p&gt;
&lt;p&gt;同样地查询对手的优先度招式也非常有用：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- morry(Pokemon), learns_priority(Pokemon, Move, Priority).
Pokemon = mawilemega, Move = suckerpunch, Priority = 1
; Pokemon = walkingwake, Move = aquajet, Priority = 1
; ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;此时，Morry 提了一个挑战：恶作剧之心特性的宝可梦，其变化招式额外获得 +1 优先度。能否在规则中体现？&lt;/p&gt;
&lt;p&gt;我的队伍恰好有一只这样的宝可梦——Tornadus。使用 Prolog 的 if/then 结构 &lt;code&gt;-&amp;gt;/2&lt;/code&gt;，三分钟搞定：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-prolog" data-lang="prolog"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;learns_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;learns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;\+&lt;/span&gt; &lt;span class="nf"&gt;doubles_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;\+&lt;/span&gt; &lt;span class="nf"&gt;protection_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Move&lt;/span&gt; &lt;span class="s"&gt;\=&lt;/span&gt; &lt;span class="s"&gt;bide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;move_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;BasePriority&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;pokemon_ability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;prankster&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;move_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt; &lt;span class="s"&gt;#=&lt;/span&gt; &lt;span class="nv"&gt;BasePriority&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt; &lt;span class="s"&gt;#=&lt;/span&gt; &lt;span class="nv"&gt;BasePriority&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Priority&lt;/span&gt; &lt;span class="s"&gt;#&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;现在查询会包含 Tornadus 所有变化招式的提升后优先度。&lt;/p&gt;
&lt;h2&gt;与电子表格的比较&lt;span class="hx:absolute hx:-mt-20" id="与电子表格的比较"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%8e%e7%94%b5%e5%ad%90%e8%a1%a8%e6%a0%bc%e7%9a%84%e6%af%94%e8%be%83" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;宝可梦社区已经有类似的工具，构建在最优秀的编程界面上：朴素的电子表格。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/pokemon/matchup-sheet.png" alt="Techno 的准备文档" loading="lazy" /&gt;&lt;/p&gt;
&lt;figcaption class="post-caption"&gt;Techno 的准备文档，一份功能强大的 Google Sheets。&lt;/figcaption&gt;
&lt;p&gt;我使用一份被称为&amp;quot;Techno&amp;rsquo;s Prep Doc&amp;quot;的 Google Sheets，输入队伍后自动生成海量对阵信息。&lt;/p&gt;
&lt;p&gt;我好奇它的优先度招式查找公式是怎样的。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/pokemon/priority-backend-list.png" alt="优先级招式后端列表" loading="lazy" /&gt;&lt;/p&gt;
&lt;figcaption class="post-caption"&gt;电子表格后端的硬编码招式列表。&lt;/figcaption&gt;
&lt;p&gt;结果是——相当复杂：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;={IFERROR(ARRAYFORMULA(VLOOKUP(FILTER(INDIRECT(Matchup!$S$3&amp;amp;&amp;#34;!$AV$4:$AV&amp;#34;),
INDIRECT(Matchup!$S$3&amp;amp;&amp;#34;!$AT$4:$AT&amp;#34;)=&amp;#34;X&amp;#34;),
{Backend!$L$2:$L,Backend!$F$2:$F},2,FALSE))),
IFERROR(FILTER(INDIRECT(Matchup!$S$3&amp;amp;&amp;#34;!$AW$4:$AW&amp;#34;),
INDIRECT(Matchup!$S$3&amp;amp;&amp;#34;!$AT$4:$AT&amp;#34;)=&amp;#34;X&amp;#34;))}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Prolog 的范式显然更具可扩展性。电子表格后端是一个硬编码的显要招式列表，而我的数据库可以查找&lt;strong&gt;任何&lt;/strong&gt;招式。这个查询——找出 Tornadus 学会的、对 Justin 队伍中任何成员效果绝佳的特殊招式——在现有工具中根本不存在。我只用了 30 秒就拼出来了：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;?- justin(Target), learns(tornadus, Move),
super_effective_move(Move, Target), move_category(Move, special).
Target = charizardmegay, Move = chillingwater
; Target = scizor, Move = heatwave
; Target = scizor, Move = incinerate
; Target = screamtail, Move = sludgebomb
; ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;最后的思考&lt;span class="hx:absolute hx:-mt-20" id="最后的思考"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%80%e5%90%8e%e7%9a%84%e6%80%9d%e8%80%83" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个项目始于一个玩笑——&amp;ldquo;如果我拿 Prolog 来做宝可梦组队会怎样&amp;rdquo;——但它教给我的逻辑编程知识比任何教科书都多。&lt;/p&gt;
&lt;p&gt;我最大的领悟是：&lt;strong&gt;Prolog 迫使你思考什么是真的，而不是做什么。&lt;/strong&gt; 在命令式编程中，大部分精力花在控制流上。在 Prolog 中，你只需陈述什么是真的，让求解器处理剩下的事。&lt;/p&gt;
&lt;p&gt;对于一个有着数千条互动规则的规则引擎（比如宝可梦对战），这简直是天启。与其写 if/else 链检查属性相克，不如声明属性表，让 Prolog 处理其余部分。&lt;/p&gt;
&lt;p&gt;如果你感兴趣，推荐：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安装 SWI-Prolog 并玩玩 &lt;a href="https://github.com/alexpetros/prologdex"target="_blank" rel="noopener"&gt;prologdex&lt;/a&gt; 数据集&lt;/li&gt;
&lt;li&gt;阅读 &lt;a href="http://www.amzi.com/AdventureInProlog/"target="_blank" rel="noopener"&gt;Adventure in Prolog&lt;/a&gt; 教程&lt;/li&gt;
&lt;li&gt;用 Prolog 对你熟悉的领域建模——它会彻底改变你对数据和关系的思考方式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;祝编码愉快，愿你的属性相克永远是效果绝佳。&lt;/p&gt;</description></item><item><title>AI 生产力的唯一落点</title><link>https://fukun.net/blog/ai-productivity-observation/</link><pubDate>Sun, 17 May 2026 18:10:00 +0800</pubDate><guid>https://fukun.net/blog/ai-productivity-observation/</guid><description>
&lt;p&gt;晚上和炎明吃饭，聊到 AI 的实际应用。几杯酒下去，话题从宏大叙事落到日常体验。我俩有个共同感受：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI 目前能有生产力的，只有 coding 这一项。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个残酷但诚实的判断。&lt;/p&gt;
&lt;h2&gt;为什么只有 Coding&lt;span class="hx:absolute hx:-mt-20" id="为什么只有-coding"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%8f%aa%e6%9c%89-coding" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;仔细想想，Coding 和其他场景有什么本质不同？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一，输出可验证。&lt;/strong&gt; 代码跑不跑得通，一运行就知道。结果客观、即时、无歧义。AI 写对了就是对了，错了就是错了，不存在&amp;quot;写得好不好&amp;quot;的主观拉扯。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二，反馈闭环极短。&lt;/strong&gt; 写一段代码 → 编译/运行 → 看到结果 → 修改，这个循环可以秒级完成。AI 和开发者之间形成了高频的&amp;quot;对话-验证-迭代&amp;quot;机制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三，错误可容忍。&lt;/strong&gt; 代码写错了不会死人。你可以反复试错，直到跑通为止。这种低代价的试错环境，是 AI 学习和人类协作最舒服的区间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四，人类判断力最低介入。&lt;/strong&gt; 代码的逻辑正确性由机器判定，不需要专家逐行审阅。非技术类任务（写文章、做设计、决策建议）都需要人的判断力做最终把关——这恰恰是最稀缺的。&lt;/p&gt;
&lt;h2&gt;其他场景为什么不行&lt;span class="hx:absolute hx:-mt-20" id="其他场景为什么不行"&gt;&lt;/span&gt;
&lt;a href="#%e5%85%b6%e4%bb%96%e5%9c%ba%e6%99%af%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%a1%8c" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;写文章&lt;/strong&gt; → 需要人类判断风格、语气、事实准确度，改了半个小时不如自己写&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据分析&lt;/strong&gt; → AI 能跑数，但&amp;quot;问对问题&amp;quot;才是关键，而这个恰恰是 AI 做不到的&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创意设计&lt;/strong&gt; → 生成一堆选项，人类从中选一个最不差的，本质是降本不是提效&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聊天陪伴&lt;/strong&gt; → 有情绪价值，但没有产出，不算生产力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些场景的共同问题：&lt;strong&gt;判断成本转移了，但没有消失。&lt;/strong&gt; 写文章的时候，AI 帮你省了打字的力气，但审稿的时间一点没少。效率提升只是把体力活变成了脑力活。&lt;/p&gt;
&lt;h2&gt;Coding 也是例外，也是范式&lt;span class="hx:absolute hx:-mt-20" id="coding-也是例外也是范式"&gt;&lt;/span&gt;
&lt;a href="#coding-%e4%b9%9f%e6%98%af%e4%be%8b%e5%a4%96%e4%b9%9f%e6%98%af%e8%8c%83%e5%bc%8f" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;但仔细想，&lt;strong&gt;未来真正有生产力的 AI 场景，都会呈现出某种&amp;quot;可验证性&amp;quot;&lt;/strong&gt;——无论是否在写代码。&lt;/p&gt;
&lt;p&gt;比如合同审查：如果有一套明确的合规规则，AI 可以逐条校验；比如医疗影像：如果有标注好的数据集，AI 可以辅助诊断。这些场景的共同特征都是：&lt;strong&gt;输出可以被客观判定对错。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而那些依赖人类主观判断的领域——策略、审美、人情世故——AI 很难产出真正的生产力。不是技术问题，是评价标准的问题。&lt;/p&gt;
&lt;h2&gt;写在最后&lt;span class="hx:absolute hx:-mt-20" id="写在最后"&gt;&lt;/span&gt;
&lt;a href="#%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;和炎明的一顿饭，让我更清楚了一件事：&lt;strong&gt;AI 不是万能工具，它是一把专门在&amp;quot;可验证&amp;quot;领域发力的扳手。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;承认它的边界，比鼓吹它的全能，更有意义。&lt;/p&gt;
&lt;p&gt;而当前这把扳手最趁手的活，就是 coding。这也是为什么从大厂到个人开发者，都在往这个方向猛押注——不是跟风，是因为这里真的能看到产出。&lt;/p&gt;</description></item><item><title>家庭网络与 NAS 搭建方案</title><link>https://fukun.net/blog/home-network-nas/</link><pubDate>Sun, 17 May 2026 15:00:00 +0800</pubDate><guid>https://fukun.net/blog/home-network-nas/</guid><description>
&lt;p&gt;折腾家庭网络的终点是克制。和 Linux 桌面美化一样，容易用力过猛。本文记录一套低成本但够用的家庭网络方案。&lt;/p&gt;
&lt;h2&gt;网络拓扑&lt;span class="hx:absolute hx:-mt-20" id="网络拓扑"&gt;&lt;/span&gt;
&lt;a href="#%e7%bd%91%e7%bb%9c%e6%8b%93%e6%89%91" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;光猫 → 软路由（OpenWrt）→ 交换机
├── Wi-Fi 6 AP ×2
├── NAS（TrueNAS）
└── 有线设备&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;软路由选 J4125 或 N100 这类低功耗 x86，刷 OpenWrt，负责拨号、DHCP、科学上网。AP 选 Wi-Fi 6 的，TP-Link XDR 系列性价比不错，刷 OpenWrt 或者用原厂固件都可以。&lt;/p&gt;
&lt;p&gt;两个 AP 组 Mesh，不需要折腾 AC 控制器。家里 120 平米基本无死角覆盖。&lt;/p&gt;
&lt;h2&gt;NAS 硬件与系统&lt;span class="hx:absolute hx:-mt-20" id="nas-硬件与系统"&gt;&lt;/span&gt;
&lt;a href="#nas-%e7%a1%ac%e4%bb%b6%e4%b8%8e%e7%b3%bb%e7%bb%9f" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;旧电脑加几块硬盘就是一台 NAS。电费要算：不要拿老 i7 加独显当 NAS，待机功耗四五十瓦，一个月电费够买半块硬盘。&lt;/p&gt;
&lt;p&gt;推荐方案：J4125 或 N100 小板 + 8GB 内存 + 2~4 块 4TB 硬盘组 RAID-Z1。系统选 TrueNAS Scale，基于 Linux，容器和虚拟机支持比 Core 版更好。待机功耗 15W 左右，一年电费不到一百块。&lt;/p&gt;
&lt;h2&gt;软件方案&lt;span class="hx:absolute hx:-mt-20" id="软件方案"&gt;&lt;/span&gt;
&lt;a href="#%e8%bd%af%e4%bb%b6%e6%96%b9%e6%a1%88" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;三个核心应用：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;软件&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;th&gt;替代方案&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;媒体服务器，自动刮削海报和元数据&lt;/td&gt;
&lt;td&gt;Jellyfin（开源免费）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Syncthing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;文件同步，多设备自动备份照片和文档&lt;/td&gt;
&lt;td&gt;Resilio Sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tailscale&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;虚拟局域网，外面也能访问家里的服务&lt;/td&gt;
&lt;td&gt;ZeroTier、WireGuard 裸配&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Plex 装在 TrueNAS 的 App 里，显卡直通给 Plex 做硬件转码——手机上用较低码率看片时自动转码，画质损失不大。Syncthing 每台设备装一个，照片目录设为&amp;quot;仅发送&amp;quot;，电脑上的文档文件夹设为&amp;quot;双向同步&amp;quot;。&lt;/p&gt;
&lt;p&gt;Tailscale 是真正的体验质变。不需要公网 IP，不需要端口转发，不需要记 IP 地址。手机、笔记本、NAS 在同一个虚拟局域网里，出门在外访问 Plex 和 NAS 文件就像在家一样。免费版支持最多 100 台设备，个人用完全够。&lt;/p&gt;
&lt;h2&gt;总结&lt;span class="hx:absolute hx:-mt-20" id="总结"&gt;&lt;/span&gt;
&lt;a href="#%e6%80%bb%e7%bb%93" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;方案的核心思路：&lt;strong&gt;够用即可&lt;/strong&gt;。不要想着一步到位，先从旧硬件开始跑起来，哪里不够补哪里。家庭网络的价值在于稳定无声地跑着，不是折腾本身。&lt;/p&gt;</description></item><item><title>Rust Error Handling: From unwrap to anyhow</title><link>https://fukun.net/blog/rust-error-handling/</link><pubDate>Sat, 16 May 2026 13:00:00 +0800</pubDate><guid>https://fukun.net/blog/rust-error-handling/</guid><description>
&lt;p&gt;Rust forces you to think about errors. Starting with &lt;code&gt;unwrap()&lt;/code&gt; everywhere is fine for prototypes, but production code needs proper error types.&lt;/p&gt;
&lt;h2&gt;Stage 1: The Unwrap Phase&lt;span class="hx:absolute hx:-mt-20" id="stage-1-the-unwrap-phase"&gt;&lt;/span&gt;
&lt;a href="#stage-1-the-unwrap-phase" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_config&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Every Rust beginner goes through this. It&amp;rsquo;s fine for scripts and prototypes — if something fails, the panic message with a line number is good enough. The problem starts when you need graceful error handling or meaningful error messages for users.&lt;/p&gt;
&lt;h2&gt;Stage 2: Box&lt;dyn Error&gt;&lt;span class="hx:absolute hx:-mt-20" id="stage-2-box"&gt;&lt;/span&gt;
&lt;a href="#stage-2-box" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The quick upgrade path:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_thing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;error&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;?&lt;/code&gt; operator works with any error type that implements &lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt;. &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; catches everything. But you lose type information — the caller can&amp;rsquo;t match on specific error variants.&lt;/p&gt;
&lt;h2&gt;Stage 3: Proper Error Types with thiserror&lt;span class="hx:absolute hx:-mt-20" id="stage-3-proper-error-types-with-thiserror"&gt;&lt;/span&gt;
&lt;a href="#stage-3-proper-error-types-with-thiserror" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For libraries, define explicit error types:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thiserror&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[derive(Error, Debug)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;AppError&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;configuration error: {0}&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;database error: {0}&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;#[from]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqlx&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;not found: {0}&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;#[from]&lt;/code&gt; attribute auto-implements &lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt;, so &lt;code&gt;?&lt;/code&gt; works seamlessly. Callers can match on variants:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do_thing&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppError&lt;/span&gt;::&lt;span class="n"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* handle 404 */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* log and return 500 */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* success */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Stage 4: Application Errors with anyhow&lt;span class="hx:absolute hx:-mt-20" id="stage-4-application-errors-with-anyhow"&gt;&lt;/span&gt;
&lt;a href="#stage-4-application-errors-with-anyhow" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For binaries, &lt;code&gt;anyhow&lt;/code&gt; provides flexible error handling without the ceremony:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;anyhow&lt;/span&gt;::&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;failed to read config file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;database connection failed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;context()&lt;/code&gt; and &lt;code&gt;with_context()&lt;/code&gt; attach human-readable messages at each fallible step. The error output is a chain of contexts, making debugging straightforward.&lt;/p&gt;
&lt;h2&gt;The Ecosystem Split&lt;span class="hx:absolute hx:-mt-20" id="the-ecosystem-split"&gt;&lt;/span&gt;
&lt;a href="#the-ecosystem-split" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Key Feature&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;thiserror&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Library crates&lt;/td&gt;
&lt;td&gt;Derive macro, typed errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;anyhow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Application binaries&lt;/td&gt;
&lt;td&gt;Context chains, easy &lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eyre&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applications (alternative)&lt;/td&gt;
&lt;td&gt;Colorful error reports, custom hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The ecosystem has settled on this pragmatic split: &lt;strong&gt;thiserror for libraries, anyhow for binaries&lt;/strong&gt;. Libraries expose typed errors so callers can react programmatically; applications benefit from rich, human-readable error chains.&lt;/p&gt;
&lt;p&gt;Remember: &lt;code&gt;unwrap()&lt;/code&gt; and &lt;code&gt;expect()&lt;/code&gt; still have their place — in tests, in examples, and when an invariant violation truly merits a panic. But for production code paths, make errors part of your type signature.&lt;/p&gt;</description></item><item><title>Hugo 站点中嵌入视频与图片的最佳实践</title><link>https://fukun.net/blog/media-test/</link><pubDate>Sat, 16 May 2026 12:00:00 +0800</pubDate><guid>https://fukun.net/blog/media-test/</guid><description>
&lt;p&gt;静态博客常被认为只能承载纯文本，但实际上 Hugo 对多媒体内容的支持相当完整。本文记录本站的视频嵌入和图片排版方案，供有类似需求的读者参考。&lt;/p&gt;
&lt;h2&gt;B 站视频嵌入&lt;span class="hx:absolute hx:-mt-20" id="b-站视频嵌入"&gt;&lt;/span&gt;
&lt;a href="#b-%e7%ab%99%e8%a7%86%e9%a2%91%e5%b5%8c%e5%85%a5" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Hugo 的 &lt;code&gt;unsafe: true&lt;/code&gt; 配置允许直接插入 &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;，这让嵌入第三方播放器变得简单。下面是本站文章中使用的 B 站视频嵌入代码：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 75%; height: 0; overflow: hidden; margin: 1.5rem 0;"&gt;
&lt;iframe
src="//player.bilibili.com/player.html?bvid=BV1AWRVB2EAz&amp;page=1&amp;high_quality=1&amp;autoplay=0"
scrolling="no"
border="0"
frameborder="no"
framespacing="0"
allowfullscreen="true"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;几个关键参数：&lt;code&gt;high_quality=1&lt;/code&gt; 默认启用高清画质，&lt;code&gt;autoplay=0&lt;/code&gt; 禁用自动播放（避免对读者造成打扰）。外层容器的 &lt;code&gt;padding-bottom: 75%&lt;/code&gt; 是 4:3 比例，如果视频是 16:9 改成 &lt;code&gt;56.25%&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2&gt;图片排版实践&lt;span class="hx:absolute hx:-mt-20" id="图片排版实践"&gt;&lt;/span&gt;
&lt;a href="#%e5%9b%be%e7%89%87%e6%8e%92%e7%89%88%e5%ae%9e%e8%b7%b5" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;宽幅配图&lt;span class="hx:absolute hx:-mt-20" id="宽幅配图"&gt;&lt;/span&gt;
&lt;a href="#%e5%ae%bd%e5%b9%85%e9%85%8d%e5%9b%be" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;对于需要烘托氛围的宽幅图片，直接使用 Markdown 默认语法即可，Hugo 会自动处理响应式缩放：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/ai.jpg" alt="AI 主题图" loading="lazy" /&gt;
&lt;figcaption class="post-caption"&gt;宽幅配图适合放在文章开头，作为视觉呼吸点。&lt;/figcaption&gt;
&lt;/p&gt;
&lt;p&gt;这类图片适合放在文章开头或章节之间作为视觉呼吸点。&lt;/p&gt;
&lt;h3&gt;多图并列&lt;span class="hx:absolute hx:-mt-20" id="多图并列"&gt;&lt;/span&gt;
&lt;a href="#%e5%a4%9a%e5%9b%be%e5%b9%b6%e5%88%97" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;需要对比展示时，可以将图片连续排列。配合 Hugo 的渲染引擎，相邻图片会自动获得合适的间距：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/coding.jpg" alt="编程" loading="lazy" /&gt;
&lt;figcaption class="post-caption"&gt;编程与思考。&lt;/figcaption&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/thinking.jpg" alt="思考" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3&gt;配图居中&lt;span class="hx:absolute hx:-mt-20" id="配图居中"&gt;&lt;/span&gt;
&lt;a href="#%e9%85%8d%e5%9b%be%e5%b1%85%e4%b8%ad" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;适量使用图片比堆砌更能提升阅读体验。一张位置恰当的配图可以给段落之间留出自然的停顿：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fukun.net/images/journal.jpg" alt="子弹日记" loading="lazy" /&gt;
&lt;figcaption class="post-caption"&gt;一张位置恰当的配图，给段落之间留出自然的停顿。&lt;/figcaption&gt;
&lt;/p&gt;
&lt;h2&gt;多媒体文件的组织&lt;span class="hx:absolute hx:-mt-20" id="多媒体文件的组织"&gt;&lt;/span&gt;
&lt;a href="#%e5%a4%9a%e5%aa%92%e4%bd%93%e6%96%87%e4%bb%b6%e7%9a%84%e7%bb%84%e7%bb%87" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;本站将所有图片和字体统一放入 &lt;code&gt;static/&lt;/code&gt; 目录，引用时使用绝对路径 &lt;code&gt;/images/xxx.jpg&lt;/code&gt;。这样做的好处是路径清晰、不受页面层级影响，也方便后续迁移到 CDN。&lt;/p&gt;
&lt;p&gt;如果你也在用 Hugo 搭建个人站点，多媒体内容的处理原则就一条：&lt;strong&gt;路径统一、格式克制、体积有数&lt;/strong&gt;。祝搭建愉快。&lt;/p&gt;</description></item><item><title>善假说</title><link>https://fukun.net/blog/ai-assistant-first-week/</link><pubDate>Sat, 16 May 2026 01:30:00 +0800</pubDate><guid>https://fukun.net/blog/ai-assistant-first-week/</guid><description>
&lt;figure&gt;&lt;img src="https://fukun.net/images/fusheng.jpg"
alt="杜堇《伏生授经图》"&gt;&lt;figcaption&gt;
&lt;p&gt;杜堇《伏生授经图》，明，大都会艺术博物馆藏&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;荀子说，君子生非异也，善假于物也。我以前读完也就过了，没细想。&lt;/p&gt;
&lt;p&gt;AI 这两年用得不少，ChatGPT、Claude、DeepSeek 都试过。问问题、写代码、润文字，确实方便。但模式始终是单向的——它等你问，你问它答，问完结束。&lt;/p&gt;
&lt;p&gt;直到我用上一个能自己干活的智能体。它叫&amp;quot;爪子&amp;quot;，跑在腾讯云一台轻量服务器上，4 核 4G，配置不高。搭起来很简单：在服务器上装了一个叫 OpenClaw 的智能体框架，绑好微信，配上 Todoist 和 Git 的 API 密钥，就能远程使唤了。这东西驻扎在服务器上，能直接执行 Shell 命令、读写文件、操作 Git 仓库、浏览网页。从有想法到跑起来，花了半晚上。&lt;/p&gt;
&lt;h2&gt;役物和任人&lt;span class="hx:absolute hx:-mt-20" id="役物和任人"&gt;&lt;/span&gt;
&lt;a href="#%e5%bd%b9%e7%89%a9%e5%92%8c%e4%bb%bb%e4%ba%ba" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;同样是 AI，心态不同，用法天差地别。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;役物&lt;/strong&gt;——问一句答一句，答错了你纠，纠完再问。你是指挥，还得盯着每一步。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;任人&lt;/strong&gt;——说清楚方向，交给它去做。做完验收就行，中间过程不必过问。&lt;/p&gt;
&lt;p&gt;前者把你绑在操作台前，后者让你从执行中抽身。区别不在技术，在心法。&lt;/p&gt;
&lt;h2&gt;三件事&lt;span class="hx:absolute hx:-mt-20" id="三件事"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%89%e4%bb%b6%e4%ba%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;试了五天，挑三件说说。&lt;/p&gt;
&lt;h3&gt;博客&lt;span class="hx:absolute hx:-mt-20" id="博客"&gt;&lt;/span&gt;
&lt;a href="#%e5%8d%9a%e5%ae%a2" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;午间忽想建站，交代了一句。它 SSH 上服务器就开始干活。&lt;/p&gt;
&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
flowchart LR
A[我：建个博客] --&amp;gt; B[SSH 登录服务器]
B --&amp;gt; C[安装 Hugo + Nginx]
C --&amp;gt; D[配置 HTTPS]
D --&amp;gt; E{测试通过？}
E --&amp;gt;|否| F[自修权限问题]
F --&amp;gt; D
E --&amp;gt;|是| G[回复就绪]
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;装 Hugo、配 Nginx、设 HTTPS、修 404 错误，全是它自己弄的。后来主题换了三次——PaperMod、terminal、Hextra——都是半夜一句话的事。第二天醒来网站已经跑在新主题上了。&lt;/p&gt;
&lt;h3&gt;资讯推送&lt;span class="hx:absolute hx:-mt-20" id="资讯推送"&gt;&lt;/span&gt;
&lt;a href="#%e8%b5%84%e8%ae%af%e6%8e%a8%e9%80%81" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;每天早上想看国际新闻、科技动态和通信消息。我让它写了个定时脚本，每天 8:30 推送微信。&lt;/p&gt;
&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
flowchart LR
A[Cron 8:30] --&amp;gt; B[拉取待办 + 新闻]
B --&amp;gt; C[编译简报]
C --&amp;gt; D[推送微信]
D --&amp;gt; E{成功？}
E --&amp;gt;|失败| F[自查超时]
F --&amp;gt; G[延长时限重发]
G --&amp;gt; D
E --&amp;gt;|成功| H[记入日志]
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;第三天推送没来。没等我问，它已经查了日志，发现 DeepSeek API 响应变慢导致超时，自动调长时限重发了。&lt;/p&gt;
&lt;h3&gt;工单管理&lt;span class="hx:absolute hx:-mt-20" id="工单管理"&gt;&lt;/span&gt;
&lt;a href="#%e5%b7%a5%e5%8d%95%e7%ae%a1%e7%90%86" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;工作对话截图丢过去，说一句&amp;quot;入待办&amp;quot;。&lt;/p&gt;
&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
flowchart LR
A[我：发聊天截图] --&amp;gt; B[读取并提取信息]
B --&amp;gt; C[创建 Todoist 任务]
C --&amp;gt; D[标优先级 + 截止日]
D --&amp;gt; E[回复确认]
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;读图、识人、提取待办、写入 Todoist、标优先级、定截止期。原来要折腾一分钟的事，现在十秒。&lt;/p&gt;
&lt;h2&gt;信任是试出来的&lt;span class="hx:absolute hx:-mt-20" id="信任是试出来的"&gt;&lt;/span&gt;
&lt;a href="#%e4%bf%a1%e4%bb%bb%e6%98%af%e8%af%95%e5%87%ba%e6%9d%a5%e7%9a%84" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一开始不放心，只让它看看日志、查查状态。后来发现出错能自愈、操作有日志可查、半夜发消息也能秒回——慢慢就放手了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;邮件过滤。&lt;/strong&gt; 信任更进一步，我给了它 IMAP 权限，让它每天扫收件箱。&lt;/p&gt;
&lt;div role="img" aria-label="图表"&gt;
&lt;pre class="mermaid hx:mt-6"&gt;
flowchart LR
A[每日扫描收件箱] --&amp;gt; B{发给我但&amp;lt;br&amp;gt;没给下属？}
B --&amp;gt;|是| C[建文件夹存档]
C --&amp;gt; D[附件存 / 正文转 md]
D --&amp;gt; E[撰写摘要]
E --&amp;gt; F[关联已有文件信息]
F --&amp;gt; G[入待办跟进]
B --&amp;gt;|否| H[跳过]
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;筛出发给我但没抄送下属的邮件，拆附件、正文转 markdown、写摘要、关联已有资料，分类归档到我本地工作目录的 inbox。第二天上班打开文件夹，摘要和附件已经摆好了。&lt;/p&gt;
&lt;h2&gt;写在最后&lt;span class="hx:absolute hx:-mt-20" id="写在最后"&gt;&lt;/span&gt;
&lt;a href="#%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;有人问：这和 ChatGPT 有什么区别？&lt;/p&gt;
&lt;p&gt;我说：&lt;strong&gt;ChatGPT 是字典，这是秘书。&lt;/strong&gt; 字典再全，不会替你写材料。秘书不一定比你懂行，但会替你跑腿。&lt;/p&gt;
&lt;p&gt;需要什么投入？一台低配云服务器，一套 API 凭证，花半晚上搭环境。门槛不高。&lt;/p&gt;
&lt;p&gt;回头看荀子那句话——&amp;ldquo;善假于物&amp;rdquo;。最好的工具不是功能最强的，而是能让你从琐事中脱身的。用到某个程度你会发现，真正费脑子的不是&amp;quot;怎么问 AI&amp;quot;，而是&amp;quot;什么可以交给 AI&amp;quot;。&lt;/p&gt;
&lt;p&gt;这念头一转，效率的瓶颈就从工具变成了自己的想象力。&lt;/p&gt;</description></item><item><title>Go Concurrency Patterns in Practice</title><link>https://fukun.net/blog/go-concurrency/</link><pubDate>Fri, 15 May 2026 10:00:00 +0800</pubDate><guid>https://fukun.net/blog/go-concurrency/</guid><description>
&lt;p&gt;I&amp;rsquo;ve been writing Go for a few years now, and concurrency remains one of those topics where textbook knowledge doesn&amp;rsquo;t quite prepare you for production. Here are a few patterns I&amp;rsquo;ve found genuinely useful.&lt;/p&gt;
&lt;h2&gt;The Worker Pool&lt;span class="hx:absolute hx:-mt-20" id="the-worker-pool"&gt;&lt;/span&gt;
&lt;a href="#the-worker-pool" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The classic. Useful when you need bounded parallelism:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;workerPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Context Propagation&lt;span class="hx:absolute hx:-mt-20" id="context-propagation"&gt;&lt;/span&gt;
&lt;a href="#context-propagation" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Probably the single most important practice I&amp;rsquo;ve adopted: &lt;strong&gt;every long-running goroutine should accept a context&lt;/strong&gt;. It makes cancellation and timeout handling explicit rather than an afterthought.&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetchWithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewRequestWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;The Select Loop&lt;span class="hx:absolute hx:-mt-20" id="the-select-loop"&gt;&lt;/span&gt;
&lt;a href="#the-select-loop" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When you need to coordinate multiple channels, &lt;code&gt;select&lt;/code&gt; is your friend. A common pattern:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Things I Wish I Knew Earlier&lt;span class="hx:absolute hx:-mt-20" id="things-i-wish-i-knew-earlier"&gt;&lt;/span&gt;
&lt;a href="#things-i-wish-i-knew-earlier" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Channel ownership&lt;/strong&gt; — the goroutine that writes should also close the channel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffered channels aren&amp;rsquo;t a fix for deadlocks&lt;/strong&gt; — they just push the problem further out&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;sync.WaitGroup&lt;/code&gt; copies by value&lt;/strong&gt; — pass it by pointer, always&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The race detector is free&lt;/strong&gt; — &lt;code&gt;go test -race&lt;/code&gt; should be part of your CI&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Concurrency in Go is &lt;em&gt;easy to write&lt;/em&gt; but &lt;em&gt;hard to get right&lt;/em&gt;. The compiler won&amp;rsquo;t save you from logical races. That&amp;rsquo;s on us.&lt;/p&gt;</description></item><item><title>CI/CD Patterns with GitHub Actions</title><link>https://fukun.net/blog/cicd-github-actions/</link><pubDate>Fri, 15 May 2026 09:00:00 +0800</pubDate><guid>https://fukun.net/blog/cicd-github-actions/</guid><description>
&lt;p&gt;GitHub Actions is now the default CI for most open-source projects. Build matrices save config lines, caching &lt;code&gt;node_modules&lt;/code&gt; and Go modules saves minutes per run. Reusable workflows keep things DRY across repos.&lt;/p&gt;
&lt;h2&gt;Build Matrices&lt;span class="hx:absolute hx:-mt-20" id="build-matrices"&gt;&lt;/span&gt;
&lt;a href="#build-matrices" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Don&amp;rsquo;t copy-paste job definitions for different Node versions or OS targets. Use a matrix:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;os&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest, macos-latest]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ matrix.os }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/setup-node@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;node-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ matrix.node }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;npm test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This single definition expands to 6 parallel jobs. Add &lt;code&gt;fail-fast: false&lt;/code&gt; if you want all runs to complete even when one fails — helpful for catching platform-specific bugs in one CI run.&lt;/p&gt;
&lt;h2&gt;Smart Caching&lt;span class="hx:absolute hx:-mt-20" id="smart-caching"&gt;&lt;/span&gt;
&lt;a href="#smart-caching" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Caching is the easiest way to cut CI minutes:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/cache@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;~/.npm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;npm-${{ runner.os }}-${{ hashFiles(&amp;#39;package-lock.json&amp;#39;) }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restore-keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;npm-${{ runner.os }}-&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;restore-keys&lt;/code&gt; fallback is important: even a partial cache hit saves time. For Go modules, cache &lt;code&gt;~/go/pkg/mod&lt;/code&gt;. For Docker builds, use BuildKit&amp;rsquo;s registry cache (&lt;code&gt;--cache-from type=registry&lt;/code&gt;) for layer caching across CI runs.&lt;/p&gt;
&lt;h2&gt;Environment-Specific Deployments&lt;span class="hx:absolute hx:-mt-20" id="environment-specific-deployments"&gt;&lt;/span&gt;
&lt;a href="#environment-specific-deployments" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Use environments to gate production deploys:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;deploy-prod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;needs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;test, build]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;production&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./deploy.sh production&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;environment: production&lt;/code&gt; lets you set required reviewers, wait timers, and environment-specific secrets in the GitHub UI. No more accidentally deploying to prod from a feature branch.&lt;/p&gt;
&lt;h2&gt;Reusable Workflows&lt;span class="hx:absolute hx:-mt-20" id="reusable-workflows"&gt;&lt;/span&gt;
&lt;a href="#reusable-workflows" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When you maintain multiple repos, reusable workflows prevent drift:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# .github/workflows/deploy.yml in a shared repo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workflow_call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_ROLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Call it from any repo:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;org/shared-workflows/.github/workflows/deploy.yml@main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;staging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_ROLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.AWS_ROLE }}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Update the shared workflow once, and every repo benefits. This pattern scales well for orgs with 10+ services.&lt;/p&gt;
&lt;h2&gt;Practical Tips&lt;span class="hx:absolute hx:-mt-20" id="practical-tips"&gt;&lt;/span&gt;
&lt;a href="#practical-tips" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;timeout-minutes&lt;/code&gt; on every job — the default is 360 minutes, and you don&amp;rsquo;t want a hung test burning your quota.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;concurrency&lt;/code&gt; to cancel redundant runs when pushing to the same PR multiple times.&lt;/li&gt;
&lt;li&gt;Pin action versions to SHA hashes for supply-chain security, not just tags.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Vim Motions That Actually Save Time</title><link>https://fukun.net/blog/vim-motions/</link><pubDate>Thu, 14 May 2026 14:00:00 +0800</pubDate><guid>https://fukun.net/blog/vim-motions/</guid><description>
&lt;p&gt;Everyone tells you to learn Vim, but nobody tells you &lt;em&gt;which&lt;/em&gt; motions are worth the muscle memory investment. After three years of daily use, here&amp;rsquo;s my honest ranking.&lt;/p&gt;
&lt;h2&gt;Tier 1: Non-Negotiable&lt;span class="hx:absolute hx:-mt-20" id="tier-1-non-negotiable"&gt;&lt;/span&gt;
&lt;a href="#tier-1-non-negotiable" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These pay for themselves within a week:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Motion&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ci&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change inside quotes&lt;/td&gt;
&lt;td&gt;Editing strings, attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ci(&lt;/code&gt; / &lt;code&gt;ci{&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change inside brackets&lt;/td&gt;
&lt;td&gt;Refactoring arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Jump to matching bracket&lt;/td&gt;
&lt;td&gt;Navigating code blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt; / &lt;code&gt;#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search word under cursor&lt;/td&gt;
&lt;td&gt;Quick variable tracing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat last change&lt;/td&gt;
&lt;td&gt;The single biggest time-saver&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Tier 2: Worth Practicing&lt;span class="hx:absolute hx:-mt-20" id="tier-2-worth-practicing"&gt;&lt;/span&gt;
&lt;a href="#tier-2-worth-practicing" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These take a few weeks to internalize but compound:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;f{char}&lt;/code&gt; / &lt;code&gt;F{char}&lt;/code&gt; — find character inline. Faster than &lt;code&gt;h&lt;/code&gt;/&lt;code&gt;l&lt;/code&gt; spam&lt;/li&gt;
&lt;li&gt;&lt;code&gt;t{char}&lt;/code&gt; / &lt;code&gt;T{char}&lt;/code&gt; — find &lt;em&gt;until&lt;/em&gt; character. Perfect for deleting &lt;code&gt;until&lt;/code&gt; a comma&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl+d&lt;/code&gt; / &lt;code&gt;ctrl+u&lt;/code&gt; — half-page scroll. Keeps your eyes on the same spot&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zz&lt;/code&gt; — center current line. Eliminates mental reorientation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tier 3: Nice to Have&lt;span class="hx:absolute hx:-mt-20" id="tier-3-nice-to-have"&gt;&lt;/span&gt;
&lt;a href="#tier-3-nice-to-have" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Useful but not essential for daily work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gt&lt;/code&gt; / &lt;code&gt;gT&lt;/code&gt; — switch tabs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl+o&lt;/code&gt; / &lt;code&gt;ctrl+i&lt;/code&gt; — jump back/forward in the jumplist&lt;/li&gt;
&lt;li&gt;Macros (&lt;code&gt;q{register}&lt;/code&gt;) — only worth it for repetitive tasks of 10+ iterations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What I Don&amp;rsquo;t Use&lt;span class="hx:absolute hx:-mt-20" id="what-i-dont-use"&gt;&lt;/span&gt;
&lt;a href="#what-i-dont-use" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Line numbers as targets&lt;/strong&gt; (&lt;code&gt;:42&lt;/code&gt;) — too slow mentally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual block mode&lt;/strong&gt; — I use multi-cursor in VS Code instead&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;/&lt;/code&gt; search with regex&lt;/strong&gt; — I just use &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;f&lt;/code&gt; 90% of the time&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The real productivity gain isn&amp;rsquo;t in any single motion — it&amp;rsquo;s in &lt;strong&gt;not thinking about navigation&lt;/strong&gt;. When your fingers just move the cursor without conscious effort, that&amp;rsquo;s when the payoff arrives.&lt;/p&gt;</description></item><item><title>Deploying to AWS ECS with Terraform</title><link>https://fukun.net/blog/terraform-aws-ecs/</link><pubDate>Thu, 14 May 2026 10:00:00 +0800</pubDate><guid>https://fukun.net/blog/terraform-aws-ecs/</guid><description>
&lt;p&gt;ECS with Fargate is the sweet spot between managing servers and going full serverless. Terraform makes it reproducible.&lt;/p&gt;
&lt;h2&gt;The Stack&lt;span class="hx:absolute hx:-mt-20" id="the-stack"&gt;&lt;/span&gt;
&lt;a href="#the-stack" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here&amp;rsquo;s what we need to provision:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VPC&lt;/td&gt;
&lt;td&gt;Isolated network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public &amp;amp; private subnets&lt;/td&gt;
&lt;td&gt;Multi-AZ placement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security groups&lt;/td&gt;
&lt;td&gt;Network access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECR repository&lt;/td&gt;
&lt;td&gt;Container image storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS cluster + service&lt;/td&gt;
&lt;td&gt;Run containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALB + target group&lt;/td&gt;
&lt;td&gt;Traffic distribution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Security Groups: Getting Them Right&lt;span class="hx:absolute hx:-mt-20" id="security-groups-getting-them-right"&gt;&lt;/span&gt;
&lt;a href="#security-groups-getting-them-right" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The key is getting the security groups right — one misconfigured rule and your containers can&amp;rsquo;t talk to each other, or worse, your database is exposed to the internet.&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;aws_security_group&amp;#34; &amp;#34;ecs_service&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ecs-service-sg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;ingress&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; from_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; to_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; protocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; security_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;egress&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; from_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; to_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; protocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; cidr_blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0.0.0.0/0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The principle: &lt;strong&gt;allow the minimum&lt;/strong&gt;. The ALB security group allows inbound from the internet on 80/443. The ECS security group only allows inbound from the ALB. The database security group only allows inbound from ECS.&lt;/p&gt;
&lt;h2&gt;Fargate Configuration&lt;span class="hx:absolute hx:-mt-20" id="fargate-configuration"&gt;&lt;/span&gt;
&lt;a href="#fargate-configuration" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Fargate abstracts the EC2 instances. You specify CPU and memory, and AWS handles the rest. For most web services, 0.25 vCPU / 512 MB is a good starting point — scale up based on load testing, not guessing.&lt;/p&gt;
&lt;h2&gt;CI/CD Integration&lt;span class="hx:absolute hx:-mt-20" id="cicd-integration"&gt;&lt;/span&gt;
&lt;a href="#cicd-integration" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After Terraform creates the infrastructure, your CI pipeline needs to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build and tag the Docker image&lt;/li&gt;
&lt;li&gt;Push to ECR&lt;/li&gt;
&lt;li&gt;Update the ECS service with the new task definition&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use a Terraform backend with state locking — S3 + DynamoDB is the standard approach. Without state locking, two people running &lt;code&gt;terraform apply&lt;/code&gt; at the same time can corrupt your infrastructure state.&lt;/p&gt;
&lt;h2&gt;Cost Notes&lt;span class="hx:absolute hx:-mt-20" id="cost-notes"&gt;&lt;/span&gt;
&lt;a href="#cost-notes" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Fargate is more expensive per vCPU-hour than raw EC2, but you save the operational cost of patching instances and managing capacity. For small-to-medium workloads, the difference is negligible compared to the engineering time saved.&lt;/p&gt;</description></item><item><title>REST API Design: Mistakes I've Made So You Don't Have To</title><link>https://fukun.net/blog/rest-api-design/</link><pubDate>Wed, 13 May 2026 09:00:00 +0800</pubDate><guid>https://fukun.net/blog/rest-api-design/</guid><description>
&lt;p&gt;I&amp;rsquo;ve designed, built, and maintained enough REST APIs to have made most of the classic mistakes. Here&amp;rsquo;s what I wish someone had told me upfront.&lt;/p&gt;
&lt;h2&gt;1. Versioning Early&lt;span class="hx:absolute hx:-mt-20" id="1-versioning-early"&gt;&lt;/span&gt;
&lt;a href="#1-versioning-early" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Don&amp;rsquo;t wait until you need it. Version your API from day one, either in the URL (&lt;code&gt;/v1/users&lt;/code&gt;) or via the &lt;code&gt;Accept&lt;/code&gt; header. Retrofitting versioning onto an unversioned API is painful for everyone involved.&lt;/p&gt;
&lt;p&gt;My preference: &lt;strong&gt;URL-based versioning&lt;/strong&gt;. It&amp;rsquo;s visible, impossible to forget, and trivially cacheable.&lt;/p&gt;
&lt;h2&gt;2. Pagination by Default&lt;span class="hx:absolute hx:-mt-20" id="2-pagination-by-default"&gt;&lt;/span&gt;
&lt;a href="#2-pagination-by-default" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Every list endpoint should be paginated. Every. Single. One.&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pagination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cursor&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eyJpZCI6MTIzfQ==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;has_more&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;847&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ve moved from offset-based to cursor-based pagination almost everywhere. Cursors handle real-time data changes gracefully and perform better on large datasets.&lt;/p&gt;
&lt;h2&gt;3. Error Responses Should Be Consistent&lt;span class="hx:absolute hx:-mt-20" id="3-error-responses-should-be-consistent"&gt;&lt;/span&gt;
&lt;a href="#3-error-responses-should-be-consistent" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A predictable error shape means less client-side code:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;INSUFFICIENT_BALANCE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Account balance too low for this transaction&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;details&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;required&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;29.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;available&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Never expose stack traces in production. Never leak internal IDs without intent.&lt;/p&gt;
&lt;h2&gt;4. Idempotency Keys&lt;span class="hx:absolute hx:-mt-20" id="4-idempotency-keys"&gt;&lt;/span&gt;
&lt;a href="#4-idempotency-keys" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For any mutating endpoint, support an &lt;code&gt;Idempotency-Key&lt;/code&gt; header. Your payment team will thank you, and so will your users when a retry doesn&amp;rsquo;t double-charge them.&lt;/p&gt;
&lt;h2&gt;5. Think in Terms of Deprecation&lt;span class="hx:absolute hx:-mt-20" id="5-think-in-terms-of-deprecation"&gt;&lt;/span&gt;
&lt;a href="#5-think-in-terms-of-deprecation" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Add a &lt;code&gt;Deprecation&lt;/code&gt; and &lt;code&gt;Sunset&lt;/code&gt; header to old endpoints:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;Deprecation: true
Sunset: Sat, 01 Aug 2026 00:00:00 GMT&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Give consumers time to migrate, then actually follow through on removal. Keeping deprecated endpoints around &amp;ldquo;just in case&amp;rdquo; is technical debt with interest.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Good API design is mostly about &lt;strong&gt;empathy for the consumer&lt;/strong&gt;. Ask yourself: would &lt;em&gt;you&lt;/em&gt; enjoy integrating with this?&lt;/p&gt;</description></item><item><title>Migrating to Tailwind CSS v4</title><link>https://fukun.net/blog/tailwind-v4-migration/</link><pubDate>Wed, 13 May 2026 08:00:00 +0800</pubDate><guid>https://fukun.net/blog/tailwind-v4-migration/</guid><description>
&lt;p&gt;Tailwind v4 drops the JavaScript config file in favor of CSS-based configuration. The migration is mostly mechanical but there are gotchas with the &lt;code&gt;@apply&lt;/code&gt; directive and custom variants.&lt;/p&gt;
&lt;h2&gt;CSS-First Configuration&lt;span class="hx:absolute hx:-mt-20" id="css-first-configuration"&gt;&lt;/span&gt;
&lt;a href="#css-first-configuration" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The biggest change: &lt;code&gt;tailwind.config.js&lt;/code&gt; is replaced by CSS. Your design tokens live in the &lt;code&gt;@theme&lt;/code&gt; directive:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tailwindcss&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;--color-primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;2563eb&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;--color-primary-dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;1d4ed8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;--font-sans&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Inter&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;sans-serif&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;--spacing-container&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;1280px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;VSCode IntelliSense still works — the Tailwind CSS extension reads theme values from your stylesheet.&lt;/p&gt;
&lt;h2&gt;The Migration Path&lt;span class="hx:absolute hx:-mt-20" id="the-migration-path"&gt;&lt;/span&gt;
&lt;a href="#the-migration-path" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Install v4: &lt;code&gt;npm install tailwindcss@next&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run the automatic migration tool: &lt;code&gt;npx @tailwindcss/upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Manually check &lt;code&gt;@apply&lt;/code&gt; usages — the tool handles most cases, but deeply nested &lt;code&gt;@apply&lt;/code&gt; with custom utilities sometimes breaks.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;theme.extend&lt;/code&gt; in your old config with &lt;code&gt;@theme&lt;/code&gt; blocks in CSS.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;New Features Worth Using&lt;span class="hx:absolute hx:-mt-20" id="new-features-worth-using"&gt;&lt;/span&gt;
&lt;a href="#new-features-worth-using" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;Container Queries&lt;span class="hx:absolute hx:-mt-20" id="container-queries"&gt;&lt;/span&gt;
&lt;a href="#container-queries" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;No more media queries for component-level responsiveness. Tailwind v4 ships container query utilities:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;@container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;grid @lg:grid-cols-2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Dynamic Utility Values&lt;span class="hx:absolute hx:-mt-20" id="dynamic-utility-values"&gt;&lt;/span&gt;
&lt;a href="#dynamic-utility-values" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Arbitrary values are simpler: &lt;code&gt;w-(--sidebar-width)&lt;/code&gt; references a CSS custom property. No more bracket syntax for common cases.&lt;/p&gt;
&lt;h3&gt;Native Cascade Layers&lt;span class="hx:absolute hx:-mt-20" id="native-cascade-layers"&gt;&lt;/span&gt;
&lt;a href="#native-cascade-layers" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Tailwind v4 uses &lt;code&gt;@layer&lt;/code&gt; to control specificity. Base, components, and utilities are layered in the correct order — &lt;code&gt;@apply&lt;/code&gt; in component layer no longer risks specificity wars.&lt;/p&gt;
&lt;h2&gt;Gotchas&lt;span class="hx:absolute hx:-mt-20" id="gotchas"&gt;&lt;/span&gt;
&lt;a href="#gotchas" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Custom variants defined in old JS config need to be re-registered via &lt;code&gt;@variant&lt;/code&gt; in CSS.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;important&lt;/code&gt; config option now needs an explicit CSS strategy.&lt;/li&gt;
&lt;li&gt;Some plugins haven&amp;rsquo;t updated yet — check compatibility before migrating production projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, the migration took about two hours for a mid-sized project. The CSS-first approach feels more natural, and the build is noticeably faster thanks to the new Oxide engine.&lt;/p&gt;</description></item><item><title>从 5G 到算力网络：通信行业的技术演进观察</title><link>https://fukun.net/blog/5g-thoughts/</link><pubDate>Tue, 12 May 2026 23:00:00 +0800</pubDate><guid>https://fukun.net/blog/5g-thoughts/</guid><description>
&lt;p&gt;入行通信这些年，看着技术代际更迭，有些感触想记下来。&lt;/p&gt;
&lt;h2&gt;5G 的这五年&lt;span class="hx:absolute hx:-mt-20" id="5g-的这五年"&gt;&lt;/span&gt;
&lt;a href="#5g-%e7%9a%84%e8%bf%99%e4%ba%94%e5%b9%b4" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;5G 商用五年，回头看当初的 hype 和现实之间的差距很有意思：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;超预期&lt;/strong&gt;的部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络能力和覆盖远超 4G&lt;/li&gt;
&lt;li&gt;行业应用从概念走到落地&lt;/li&gt;
&lt;li&gt;切片、边缘计算等特性开始产生价值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;没达到预期的部分&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C 端杀手级应用迟迟未出现&lt;/li&gt;
&lt;li&gt;资费下降快，增量不增收&lt;/li&gt;
&lt;li&gt;数字化转型需求 vs 实际付费意愿之间的鸿沟&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;下一个五年：算力网络&lt;span class="hx:absolute hx:-mt-20" id="下一个五年算力网络"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%8b%e4%b8%80%e4%b8%aa%e4%ba%94%e5%b9%b4%e7%ae%97%e5%8a%9b%e7%bd%91%e7%bb%9c" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;行业现在提的更多的是「算力网络」—— 把通信网络和计算资源统一调度。&lt;/p&gt;
&lt;p&gt;几个值得关注的方向：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;云网融合&lt;/strong&gt; — 网络即服务，算力即服务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通感算一体化&lt;/strong&gt; — 通信、感知、计算在基站层面融合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 原生网络&lt;/strong&gt; — 用 AI 管网络，从辅助运维到自动运维&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低空经济&lt;/strong&gt; — 无人机、飞行汽车对网络覆盖的新需求&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;运营商面临的挑战&lt;span class="hx:absolute hx:-mt-20" id="运营商面临的挑战"&gt;&lt;/span&gt;
&lt;a href="#%e8%bf%90%e8%90%a5%e5%95%86%e9%9d%a2%e4%b8%b4%e7%9a%84%e6%8c%91%e6%88%98" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;传统语音+流量模式见顶&lt;/li&gt;
&lt;li&gt;云业务面临互联网厂商竞争&lt;/li&gt;
&lt;li&gt;组织流程迭代跟不上技术迭代速度&lt;/li&gt;
&lt;li&gt;人才结构转型压力大&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过换个角度看，通信基础设施作为数字经济的「水电煤」，长期价值不会消失，只是价值形态在变。关键是找到自己在价值链中的新位置。&lt;/p&gt;</description></item><item><title>子弹日记三年记：极简记录的力量</title><link>https://fukun.net/blog/bullet-journal/</link><pubDate>Tue, 12 May 2026 22:00:00 +0800</pubDate><guid>https://fukun.net/blog/bullet-journal/</guid><description>
&lt;p&gt;用子弹日记（Bullet Journal）三年了，从最初的跟风尝试变成现在离不开的习惯。&lt;/p&gt;
&lt;h2&gt;为什么用纸笔&lt;span class="hx:absolute hx:-mt-20" id="为什么用纸笔"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%94%a8%e7%ba%b8%e7%ac%94" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在数字化时代选择纸笔看起来有点反潮流，但正是它的「笨」反而成了最大的优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;没有通知&lt;/strong&gt; — 不会突然弹出消息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有多巴胺陷阱&lt;/strong&gt; — 不会刷着刷着就跑偏了&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;慢即是快&lt;/strong&gt; — 手写迫使你思考，而不是机械记录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;零成本启动&lt;/strong&gt; — 一本本子 + 一支笔足矣&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;我的极简体系&lt;span class="hx:absolute hx:-mt-20" id="我的极简体系"&gt;&lt;/span&gt;
&lt;a href="#%e6%88%91%e7%9a%84%e6%9e%81%e7%ae%80%e4%bd%93%e7%b3%bb" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;不搞复杂的 collections，不画花哨的装饰，保持 Ryder Carroll 原教旨风格：&lt;/p&gt;
&lt;h3&gt;月度记录（Monthly Log）&lt;span class="hx:absolute hx:-mt-20" id="月度记录monthly-log"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%88%e5%ba%a6%e8%ae%b0%e5%bd%95monthly-log" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;左侧：当月日历，标记关键日期和截止&lt;/li&gt;
&lt;li&gt;右侧：当月任务清单&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;每日记录（Daily Log）&lt;span class="hx:absolute hx:-mt-20" id="每日记录daily-log"&gt;&lt;/span&gt;
&lt;a href="#%e6%af%8f%e6%97%a5%e8%ae%b0%e5%bd%95daily-log" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;按天记录任务和笔记&lt;/li&gt;
&lt;li&gt;符号系统：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;·&lt;/code&gt; 任务&lt;/li&gt;
&lt;li&gt;&lt;code&gt;×&lt;/code&gt; 已完成&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; 迁移&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&lt;/code&gt; 计划中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;—&lt;/code&gt; 笔记&lt;/li&gt;
&lt;li&gt;&lt;code&gt;○&lt;/code&gt; 事件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;月度迁移（Monthly Migration）&lt;span class="hx:absolute hx:-mt-20" id="月度迁移monthly-migration"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%88%e5%ba%a6%e8%bf%81%e7%a7%bbmonthly-migration" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;每个月底花 15 分钟回顾：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;未完成的任务：迁移或删除&lt;/li&gt;
&lt;li&gt;当月的经验教训&lt;/li&gt;
&lt;li&gt;下个月的重点&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;跟数字工具结合&lt;span class="hx:absolute hx:-mt-20" id="跟数字工具结合"&gt;&lt;/span&gt;
&lt;a href="#%e8%b7%9f%e6%95%b0%e5%ad%97%e5%b7%a5%e5%85%b7%e7%bb%93%e5%90%88" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;纸笔做规划，数字化工具做执行和归档：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Todoist&lt;/strong&gt; 管理需要提醒的短期任务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;子弹日记&lt;/strong&gt; 做周度和月度规划&lt;/li&gt;
&lt;li&gt;互不干扰，各司其职&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三年下来最大感受：工具不重要，重要的是&lt;strong&gt;坚持记录&lt;/strong&gt;和&lt;strong&gt;定期回顾&lt;/strong&gt;。&lt;/p&gt;</description></item><item><title>2026 年 AI 工具链实用观察</title><link>https://fukun.net/blog/ai-tools-2026/</link><pubDate>Tue, 12 May 2026 21:00:00 +0800</pubDate><guid>https://fukun.net/blog/ai-tools-2026/</guid><description>
&lt;p&gt;2026 年的 AI 工具生态可以用一个词形容——&lt;strong&gt;务实&lt;/strong&gt;。经过前两年的概念爆发，现在已经进入落地阶段。&lt;/p&gt;
&lt;h2&gt;DeepSeek 实测&lt;span class="hx:absolute hx:-mt-20" id="deepseek-实测"&gt;&lt;/span&gt;
&lt;a href="#deepseek-%e5%ae%9e%e6%b5%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最近深度体验了 DeepSeek V4 系列模型，几点感受：&lt;/p&gt;
&lt;h3&gt;V4-Flash&lt;span class="hx:absolute hx:-mt-20" id="v4-flash"&gt;&lt;/span&gt;
&lt;a href="#v4-flash" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;日常使用首选。响应快、性价比高，适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码辅助和 debug&lt;/li&gt;
&lt;li&gt;文档撰写和润色&lt;/li&gt;
&lt;li&gt;信息检索和整理&lt;/li&gt;
&lt;li&gt;日常问答&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;V4-Pro&lt;span class="hx:absolute hx:-mt-20" id="v4-pro"&gt;&lt;/span&gt;
&lt;a href="#v4-pro" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;复杂场景下表现出色，适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;长文分析和总结&lt;/li&gt;
&lt;li&gt;多步推理任务&lt;/li&gt;
&lt;li&gt;代码架构设计&lt;/li&gt;
&lt;li&gt;领域专业知识问答&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;账户余额 ¥74.31，按目前用量够跑一阵子。&lt;/p&gt;
&lt;h2&gt;智能体（Agent）趋势&lt;span class="hx:absolute hx:-mt-20" id="智能体agent趋势"&gt;&lt;/span&gt;
&lt;a href="#%e6%99%ba%e8%83%bd%e4%bd%93agent%e8%b6%8b%e5%8a%bf" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;今年 Agent 是主题词。从单纯对话到能独立执行任务的智能体，有几个方向值得关注：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;编码 Agent&lt;/strong&gt; — 从写代码到提 PR，全流程自动化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文档 Agent&lt;/strong&gt; — 自动生成周报、会议纪要、汇报材料&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运维 Agent&lt;/strong&gt; — 日志分析、异常告警、故障排查&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;个人助理 Agent&lt;/strong&gt; — 日程管理、信息聚合、任务委派&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;怎么用更划算&lt;span class="hx:absolute hx:-mt-20" id="怎么用更划算"&gt;&lt;/span&gt;
&lt;a href="#%e6%80%8e%e4%b9%88%e7%94%a8%e6%9b%b4%e5%88%92%e7%ae%97" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;大部分日常任务用 Flash 就行&lt;/li&gt;
&lt;li&gt;复杂任务才上 Pro，避免资源浪费&lt;/li&gt;
&lt;li&gt;善用上下文缓存，减少重复 token 消耗&lt;/li&gt;
&lt;li&gt;配合工具链（MCP、Function Calling）发挥更大价值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI 工具的价值不在于它能做什么，而在于你用它做了什么。&lt;/p&gt;</description></item><item><title>从零搭建个人博客：Hugo + PaperMod 实践记录</title><link>https://fukun.net/blog/setup-blog/</link><pubDate>Tue, 12 May 2026 20:00:00 +0800</pubDate><guid>https://fukun.net/blog/setup-blog/</guid><description>
&lt;h2&gt;为什么选 Hugo&lt;span class="hx:absolute hx:-mt-20" id="为什么选-hugo"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%80%89-hugo" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;静态博客框架不少，选 Hugo 主要看中三点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;快&lt;/strong&gt; — 编译以毫秒计，生成纯静态文件，部署无压力&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt; — 一个二进制文件，无需 Node.js、PHP 等运行时依赖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主题丰富&lt;/strong&gt; — PaperMod 主题简洁优雅，阅读体验好&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;环境&lt;span class="hx:absolute hx:-mt-20" id="环境"&gt;&lt;/span&gt;
&lt;a href="#%e7%8e%af%e5%a2%83" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务器&lt;/strong&gt;: 腾讯云 Lighthouse，北京地域&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统&lt;/strong&gt;: Ubuntu 24.04 LTS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;域名&lt;/strong&gt;: 备案审核中&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hugo&lt;/strong&gt;: v0.161.1 (extended)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主题&lt;/strong&gt;: PaperMod v8&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;踩坑记录&lt;span class="hx:absolute hx:-mt-20" id="踩坑记录"&gt;&lt;/span&gt;
&lt;a href="#%e8%b8%a9%e5%9d%91%e8%ae%b0%e5%bd%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;GitHub 下载慢&lt;span class="hx:absolute hx:-mt-20" id="github-下载慢"&gt;&lt;/span&gt;
&lt;a href="#github-%e4%b8%8b%e8%bd%bd%e6%85%a2" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;国内 VPS 下载 GitHub 上的文件速度感人，最终通过 GitHub API 的 &lt;code&gt;/zipball/master&lt;/code&gt; 接口才成功拉下 PaperMod 主题：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -L -H &lt;span class="s2"&gt;&amp;#34;Accept: application/vnd.github.v3+json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://api.github.com/repos/adityatelange/hugo-PaperMod/zipball/master&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o papermod.zip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Snap 里的 Hugo&lt;span class="hx:absolute hx:-mt-20" id="snap-里的-hugo"&gt;&lt;/span&gt;
&lt;a href="#snap-%e9%87%8c%e7%9a%84-hugo" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;系统里 apt 源的 Hugo 版本较旧（v0.123.7），snap 里有最新的 v0.161.1 但 snap 在 LXC 容器里有 cgroup 问题。解决方式是直接从 snap 挂载目录拷贝二进制：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp /snap/hugo/current/bin/hugo /usr/local/bin/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;最终效果&lt;span class="hx:absolute hx:-mt-20" id="最终效果"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%80%e7%bb%88%e6%95%88%e6%9e%9c" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;现在博客跑着 PaperMod 主题，支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;明暗主题自动切换&lt;/li&gt;
&lt;li&gt;RSS 订阅&lt;/li&gt;
&lt;li&gt;标签分类&lt;/li&gt;
&lt;li&gt;代码高亮与复制&lt;/li&gt;
&lt;li&gt;响应式布局&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后续等备案通过后绑定域名，配上 HTTPS 正式证书就齐活了。&lt;/p&gt;</description></item><item><title>VPS 新手入门：从购买到上线一个网站</title><link>https://fukun.net/blog/vps-basics/</link><pubDate>Tue, 12 May 2026 19:00:00 +0800</pubDate><guid>https://fukun.net/blog/vps-basics/</guid><description>
&lt;p&gt;买了一台云服务器之后，怎么让它真正跑起来？这篇文章记录基本的 VPS 初始化和安全配置流程。&lt;/p&gt;
&lt;h2&gt;购买后的第一件事&lt;span class="hx:absolute hx:-mt-20" id="购买后的第一件事"&gt;&lt;/span&gt;
&lt;a href="#%e8%b4%ad%e4%b9%b0%e5%90%8e%e7%9a%84%e7%ac%ac%e4%b8%80%e4%bb%b6%e4%ba%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;拿到服务器 IP 和 root 密码后：&lt;/p&gt;
&lt;h3&gt;1. SSH 登录&lt;span class="hx:absolute hx:-mt-20" id="1-ssh-登录"&gt;&lt;/span&gt;
&lt;a href="#1-ssh-%e7%99%bb%e5%bd%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh root@你的服务器IP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;第一次登录会提示确认主机指纹，输入 &lt;code&gt;yes&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h3&gt;2. 创建普通用户&lt;span class="hx:absolute hx:-mt-20" id="2-创建普通用户"&gt;&lt;/span&gt;
&lt;a href="#2-%e5%88%9b%e5%bb%ba%e6%99%ae%e9%80%9a%e7%94%a8%e6%88%b7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;root 权限太大，日常操作应该用普通用户：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adduser 用户名
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usermod -aG sudo 用户名&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;3. 配置 SSH 密钥登录&lt;span class="hx:absolute hx:-mt-20" id="3-配置-ssh-密钥登录"&gt;&lt;/span&gt;
&lt;a href="#3-%e9%85%8d%e7%bd%ae-ssh-%e5%af%86%e9%92%a5%e7%99%bb%e5%bd%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 在本地机器上生成密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -t ed25519
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 把公钥传到服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-copy-id 用户名@服务器IP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;然后编辑 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;，关闭密码登录：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;4. 防火墙配置&lt;span class="hx:absolute hx:-mt-20" id="4-防火墙配置"&gt;&lt;/span&gt;
&lt;a href="#4-%e9%98%b2%e7%81%ab%e5%a2%99%e9%85%8d%e7%bd%ae" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;用 ufw 简单配置：&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw allow OpenSSH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw allow 80/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw allow 443/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw enable&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;5. 安装必要的软件&lt;span class="hx:absolute hx:-mt-20" id="5-安装必要的软件"&gt;&lt;/span&gt;
&lt;a href="#5-%e5%ae%89%e8%a3%85%e5%bf%85%e8%a6%81%e7%9a%84%e8%bd%af%e4%bb%b6" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install -y nginx curl wget git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;部署静态网站&lt;span class="hx:absolute hx:-mt-20" id="部署静态网站"&gt;&lt;/span&gt;
&lt;a href="#%e9%83%a8%e7%bd%b2%e9%9d%99%e6%80%81%e7%bd%91%e7%ab%99" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最简单的方式是用 Nginx：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把构建好的静态文件放到 &lt;code&gt;/var/www/html&lt;/code&gt; 或自定义目录&lt;/li&gt;
&lt;li&gt;配置 Nginx 站点文件指向该目录&lt;/li&gt;
&lt;li&gt;运行 &lt;code&gt;nginx -t &amp;amp;&amp;amp; systemctl reload nginx&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;安全底线&lt;span class="hx:absolute hx:-mt-20" id="安全底线"&gt;&lt;/span&gt;
&lt;a href="#%e5%ae%89%e5%85%a8%e5%ba%95%e7%ba%bf" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;不要用 root 直接操作日常任务&lt;/li&gt;
&lt;li&gt;关闭 SSH 密码登录&lt;/li&gt;
&lt;li&gt;定期更新系统包&lt;/li&gt;
&lt;li&gt;非必要不开放额外端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务器就像一间屋子，基础装修做好才能住得安心。&lt;/p&gt;</description></item><item><title>我的效率工具链 2026</title><link>https://fukun.net/blog/tool-setup/</link><pubDate>Tue, 12 May 2026 18:00:00 +0800</pubDate><guid>https://fukun.net/blog/tool-setup/</guid><description>
&lt;p&gt;持续折腾工具多年，今年终于稳定下来。记录一下目前的工具链，给想优化工作流的朋友参考。&lt;/p&gt;
&lt;h2&gt;笔记与知识管理&lt;span class="hx:absolute hx:-mt-20" id="笔记与知识管理"&gt;&lt;/span&gt;
&lt;a href="#%e7%ac%94%e8%ae%b0%e4%b8%8e%e7%9f%a5%e8%af%86%e7%ae%a1%e7%90%86" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;子弹日记（纸笔）&lt;/strong&gt; — 每日规划和自我对话的核心载体。具体用法之前写过，不再重复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo + Hextra&lt;/strong&gt; — 个人博客，用于对外输出。纯静态，零维护成本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; — 技术笔记和代码片段的存档。Markdown 文件 + 搜索，比任何笔记软件都好用。&lt;/p&gt;
&lt;h2&gt;任务管理&lt;span class="hx:absolute hx:-mt-20" id="任务管理"&gt;&lt;/span&gt;
&lt;a href="#%e4%bb%bb%e5%8a%a1%e7%ae%a1%e7%90%86" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Todoist&lt;/strong&gt; — 短期任务和提醒工具。优点是快速、跨平台、自然语言识别能力强。「今天要做的事」用它就够了。&lt;/p&gt;
&lt;h2&gt;AI 工具&lt;span class="hx:absolute hx:-mt-20" id="ai-工具"&gt;&lt;/span&gt;
&lt;a href="#ai-%e5%b7%a5%e5%85%b7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;DeepSeek V4&lt;/strong&gt; — 主力 AI 助手。Flash 模型日常用，Pro 模型处理复杂任务。API 接入各种场景。&lt;/p&gt;
&lt;h2&gt;开发相关&lt;span class="hx:absolute hx:-mt-20" id="开发相关"&gt;&lt;/span&gt;
&lt;a href="#%e5%bc%80%e5%8f%91%e7%9b%b8%e5%85%b3" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VS Code&lt;/strong&gt; — 主力编辑器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hugo&lt;/strong&gt; — 静态博客生成器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx&lt;/strong&gt; — Web 服务器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git&lt;/strong&gt; — 版本控制&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;选工具的原则&lt;span class="hx:absolute hx:-mt-20" id="选工具的原则"&gt;&lt;/span&gt;
&lt;a href="#%e9%80%89%e5%b7%a5%e5%85%b7%e7%9a%84%e5%8e%9f%e5%88%99" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;够用就好&lt;/strong&gt; — 不追求功能最全，追求用得顺手&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据可控&lt;/strong&gt; — 优先选能导出、能自托管的方案&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习成本低&lt;/strong&gt; — 花在学工具上的时间要能赚回来&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨平台&lt;/strong&gt; — 手机和电脑都能用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;好工具的标准不是功能多，而是让你忘记工具的存在，专注于事情本身。&lt;/p&gt;</description></item><item><title>极简主义的实用指南：少即是多</title><link>https://fukun.net/blog/minimalism/</link><pubDate>Tue, 12 May 2026 17:00:00 +0800</pubDate><guid>https://fukun.net/blog/minimalism/</guid><description>
&lt;p&gt;极简主义不是家徒四壁，而是&lt;strong&gt;去掉多余的，留下重要的&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;数字极简&lt;span class="hx:absolute hx:-mt-20" id="数字极简"&gt;&lt;/span&gt;
&lt;a href="#%e6%95%b0%e5%ad%97%e6%9e%81%e7%ae%80" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;信息过载是这个时代的通病。几个容易上手的做法：&lt;/p&gt;
&lt;h3&gt;清理通知&lt;span class="hx:absolute hx:-mt-20" id="清理通知"&gt;&lt;/span&gt;
&lt;a href="#%e6%b8%85%e7%90%86%e9%80%9a%e7%9f%a5" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;手机里 90% 的推送都不需要即时响应。关掉非必要通知，只保留电话、短信和真正重要的一两个 App。&lt;/p&gt;
&lt;h3&gt;统一信息入口&lt;span class="hx:absolute hx:-mt-20" id="统一信息入口"&gt;&lt;/span&gt;
&lt;a href="#%e7%bb%9f%e4%b8%80%e4%bf%a1%e6%81%af%e5%85%a5%e5%8f%a3" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;每天固定时间看消息，而不是随时被消息打断。可以设成上午、下午各一次。&lt;/p&gt;
&lt;h3&gt;减少「收藏即学到」&lt;span class="hx:absolute hx:-mt-20" id="减少收藏即学到"&gt;&lt;/span&gt;
&lt;a href="#%e5%87%8f%e5%b0%91%e6%94%b6%e8%97%8f%e5%8d%b3%e5%ad%a6%e5%88%b0" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;收藏夹里存了几百篇「以后再看」的文章，真正回看的不到 5%。不如看到就处理：要么现在读，要么归档待读，要么直接删。&lt;/p&gt;
&lt;h2&gt;物品极简&lt;span class="hx:absolute hx:-mt-20" id="物品极简"&gt;&lt;/span&gt;
&lt;a href="#%e7%89%a9%e5%93%81%e6%9e%81%e7%ae%80" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一个简单的判断标准：&lt;strong&gt;这件东西在过去一年里用过吗？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用过 → 保留&lt;/li&gt;
&lt;li&gt;没用到但将来可能用 → 先收起来，再给一年期限&lt;/li&gt;
&lt;li&gt;没用到将来也不会用 → 送人或扔掉&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;精神极简&lt;span class="hx:absolute hx:-mt-20" id="精神极简"&gt;&lt;/span&gt;
&lt;a href="#%e7%b2%be%e7%a5%9e%e6%9e%81%e7%ae%80" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;比物品和信息更重要的，是减少内心的负担：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;少承诺&lt;/strong&gt; — 不会做的事不要答应，做不了的事及时说&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;少比较&lt;/strong&gt; — 别人的进度和你的关系不大&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;少内耗&lt;/strong&gt; — 纠结 5 分钟还没决定的事，说明选哪个都行&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description></item><item><title>2026 上半年阅读清单与推荐</title><link>https://fukun.net/blog/reading-2026/</link><pubDate>Tue, 12 May 2026 16:00:00 +0800</pubDate><guid>https://fukun.net/blog/reading-2026/</guid><description>
&lt;p&gt;上半年读了十几本书，挑几本值得推荐的记一下。&lt;/p&gt;
&lt;h2&gt;技术类&lt;span class="hx:absolute hx:-mt-20" id="技术类"&gt;&lt;/span&gt;
&lt;a href="#%e6%8a%80%e6%9c%af%e7%b1%bb" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;《软件设计的哲学》&lt;span class="hx:absolute hx:-mt-20" id="软件设计的哲学"&gt;&lt;/span&gt;
&lt;a href="#%e8%bd%af%e4%bb%b6%e8%ae%be%e8%ae%a1%e7%9a%84%e5%93%b2%e5%ad%a6" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;不是讲设计模式的，而是讲&lt;strong&gt;复杂度管理&lt;/strong&gt;。核心观点：软件设计的本质是降低复杂度。写得深入浅出，适合有几年经验想突破瓶颈的开发者。&lt;/p&gt;
&lt;h3&gt;《凤凰项目》&lt;span class="hx:absolute hx:-mt-20" id="凤凰项目"&gt;&lt;/span&gt;
&lt;a href="#%e5%87%a4%e5%87%b0%e9%a1%b9%e7%9b%ae" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;用小说的形式讲 DevOps 转型。IT 运维的日常在书里无比真实，读起来很有代入感。&lt;/p&gt;
&lt;h2&gt;效率类&lt;span class="hx:absolute hx:-mt-20" id="效率类"&gt;&lt;/span&gt;
&lt;a href="#%e6%95%88%e7%8e%87%e7%b1%bb" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;《深度工作》&lt;span class="hx:absolute hx:-mt-20" id="深度工作"&gt;&lt;/span&gt;
&lt;a href="#%e6%b7%b1%e5%ba%a6%e5%b7%a5%e4%bd%9c" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Cal Newport 的代表作。核心论点：在这个随时被打断的时代，深度专注的能力越来越稀缺，也越来越有价值。&lt;/p&gt;
&lt;p&gt;书里给了一些具体的训练方法，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安排固定的深度工作时间段&lt;/li&gt;
&lt;li&gt;社交媒体断舍离&lt;/li&gt;
&lt;li&gt;用仪式感帮助进入状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;人文类&lt;span class="hx:absolute hx:-mt-20" id="人文类"&gt;&lt;/span&gt;
&lt;a href="#%e4%ba%ba%e6%96%87%e7%b1%bb" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;《你以为你以为的就是你以为的吗？》&lt;span class="hx:absolute hx:-mt-20" id="你以为你以为的就是你以为的吗"&gt;&lt;/span&gt;
&lt;a href="#%e4%bd%a0%e4%bb%a5%e4%b8%ba%e4%bd%a0%e4%bb%a5%e4%b8%ba%e7%9a%84%e5%b0%b1%e6%98%af%e4%bd%a0%e4%bb%a5%e4%b8%ba%e7%9a%84%e5%90%97" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;一本有意思的思维训练书，帮助识别日常思考中的逻辑漏洞。适合碎片时间翻几页。&lt;/p&gt;
&lt;h2&gt;阅读习惯&lt;span class="hx:absolute hx:-mt-20" id="阅读习惯"&gt;&lt;/span&gt;
&lt;a href="#%e9%98%85%e8%af%bb%e4%b9%a0%e6%83%af" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;今年调整了阅读方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;早上的时间给深度阅读&lt;/strong&gt; — 30 分钟，不碰手机&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通勤时间给信息类阅读&lt;/strong&gt; — RSS、Newsletter、技术文章&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;周末下午给整本书&lt;/strong&gt; — 泡杯茶，一次读一两章&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读书不求多，读完、消化、能用上，一本顶十本。&lt;/p&gt;</description></item><item><title>Python Async: Beyond the Basics</title><link>https://fukun.net/blog/python-async-guide/</link><pubDate>Tue, 12 May 2026 11:00:00 +0800</pubDate><guid>https://fukun.net/blog/python-async-guide/</guid><description>
&lt;p&gt;Async Python is no longer optional if you&amp;rsquo;re doing any kind of I/O-bound work. Beyond the basics of &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, understanding the event loop helps debug elusive issues.&lt;/p&gt;
&lt;h2&gt;The Event Loop in Practice&lt;span class="hx:absolute hx:-mt-20" id="the-event-loop-in-practice"&gt;&lt;/span&gt;
&lt;a href="#the-event-loop-in-practice" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The event loop runs one coroutine at a time, switching at &lt;code&gt;await&lt;/code&gt; points. This is cooperative multitasking — a coroutine that never awaits blocks everything else. If you have CPU-bound work inside an async function, offload it to a thread pool with &lt;code&gt;asyncio.to_thread()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Bad: blocks the event loop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# CPU-bound, no await&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Good: offloads to a thread&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;good&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Async Context Managers&lt;span class="hx:absolute hx:-mt-20" id="async-context-managers"&gt;&lt;/span&gt;
&lt;a href="#async-context-managers" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Writing proper async context managers is essential for managing connections and locks:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AsyncConnection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__aenter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__aexit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;contextlib.asynccontextmanager&lt;/code&gt; decorator makes this even cleaner for simple cases.&lt;/p&gt;
&lt;h2&gt;Common Pitfalls with &lt;code&gt;asyncio.gather&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="common-pitfalls-with-asynciogather"&gt;&lt;/span&gt;
&lt;a href="#common-pitfalls-with-asynciogather" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;gather&lt;/code&gt; runs tasks concurrently, but one failing task doesn&amp;rsquo;t cancel the others by default. If you need all-or-nothing semantics, use &lt;code&gt;asyncio.TaskGroup&lt;/code&gt; (Python 3.11+):&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskGroup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Both succeeded, or both were cancelled&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Cancel Scopes&lt;span class="hx:absolute hx:-mt-20" id="cancel-scopes"&gt;&lt;/span&gt;
&lt;a href="#cancel-scopes" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Cancellation in asyncio is cooperative: &lt;code&gt;asyncio.CancelledError&lt;/code&gt; is raised at the next &lt;code&gt;await&lt;/code&gt;. If your coroutine catches and suppresses it, the task becomes uncancellable — usually a bug. Use &lt;code&gt;asyncio.shield()&lt;/code&gt; sparingly to protect critical cleanup code.&lt;/p&gt;
&lt;h2&gt;Structured Concurrency&lt;span class="hx:absolute hx:-mt-20" id="structured-concurrency"&gt;&lt;/span&gt;
&lt;a href="#structured-concurrency" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The trend across languages (Trio in Python, Kotlin coroutines, Swift concurrency) is toward structured concurrency: every task has a clear parent scope that determines its lifetime. &lt;code&gt;TaskGroup&lt;/code&gt; is Python&amp;rsquo;s step in this direction. Embrace it over bare &lt;code&gt;create_task()&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;Async Python has matured significantly. Most of the old footguns have been addressed, but the fundamentals — understand the event loop, respect cancellation, and structure your concurrency — remain essential.&lt;/p&gt;</description></item><item><title>Linux 桌面美化指北</title><link>https://fukun.net/blog/linux-customization/</link><pubDate>Mon, 11 May 2026 10:00:00 +0800</pubDate><guid>https://fukun.net/blog/linux-customization/</guid><description>
&lt;p&gt;Linux 桌面环境的选择很多，但真正好用的配置往往很简单。本文记录从 Ubuntu 默认桌面开始，逐步调整到一套耐看、高效的配置。&lt;/p&gt;
&lt;h2&gt;主题选择&lt;span class="hx:absolute hx:-mt-20" id="主题选择"&gt;&lt;/span&gt;
&lt;a href="#%e4%b8%bb%e9%a2%98%e9%80%89%e6%8b%a9" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;白天用亮色主题降低反光，晚上切暗色保护眼睛。推荐 &lt;strong&gt;Nord&lt;/strong&gt; 和 &lt;strong&gt;Catppuccin&lt;/strong&gt; 两套配色——前者冷峻克制，后者柔和温暖，每天盯着看八小时不累。&lt;/p&gt;
&lt;p&gt;GTK 主题建议选择维护活跃的项目，避免 Gnome 大版本升级后样式错乱。Qt 应用在 Gnome 下可以用 &lt;code&gt;qt5ct&lt;/code&gt; 统一风格。&lt;/p&gt;
&lt;h2&gt;字体配置&lt;span class="hx:absolute hx:-mt-20" id="字体配置"&gt;&lt;/span&gt;
&lt;a href="#%e5%ad%97%e4%bd%93%e9%85%8d%e7%bd%ae" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;终端字体一定要等宽且清晰。推荐 &lt;strong&gt;Fira Code&lt;/strong&gt; 做编程字体，连字（ligatures）让 &lt;code&gt;=&amp;gt;&lt;/code&gt;、&lt;code&gt;!=&lt;/code&gt;、&lt;code&gt;&amp;gt;=&lt;/code&gt; 这类符号组合更易读。中文字体用 &lt;strong&gt;Noto Sans CJK&lt;/strong&gt;，和西文搭配自然。&lt;/p&gt;
&lt;h2&gt;终端与 Shell&lt;span class="hx:absolute hx:-mt-20" id="终端与-shell"&gt;&lt;/span&gt;
&lt;a href="#%e7%bb%88%e7%ab%af%e4%b8%8e-shell" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;终端模拟器推荐 &lt;strong&gt;Alacritty&lt;/strong&gt;——GPU 加速，配置文件清晰。配合 zsh + &lt;strong&gt;starship&lt;/strong&gt; 提示符，日常使用效率提升不少。Starship 的优势在于跨 shell 兼容，换到 fish 或 nu 不用重新配置。&lt;/p&gt;
&lt;h2&gt;窗口管理&lt;span class="hx:absolute hx:-mt-20" id="窗口管理"&gt;&lt;/span&gt;
&lt;a href="#%e7%aa%97%e5%8f%a3%e7%ae%a1%e7%90%86" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Gnome 默认的三指滑动切换工作区已经很好用。如果追求更极致的键盘操作，可以试试 &lt;strong&gt;Pop Shell&lt;/strong&gt; 平铺扩展，或者直接上 &lt;strong&gt;i3/sway&lt;/strong&gt;。但对大多数人来说，Gnome 的默认交互已经够用。&lt;/p&gt;
&lt;p&gt;关键原则：&lt;strong&gt;不追求花哨，追求每天盯着看不累。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>数据库索引优化实战</title><link>https://fukun.net/blog/database-indexing/</link><pubDate>Sun, 10 May 2026 16:00:00 +0800</pubDate><guid>https://fukun.net/blog/database-indexing/</guid><description>
&lt;p&gt;&lt;code&gt;EXPLAIN&lt;/code&gt; 是 DBA 的眼睛。看 &lt;code&gt;type&lt;/code&gt; 字段从 &lt;code&gt;ALL&lt;/code&gt; 变成 &lt;code&gt;ref&lt;/code&gt; 是最有成就感的事。&lt;/p&gt;
&lt;h2&gt;从慢查询开始&lt;span class="hx:absolute hx:-mt-20" id="从慢查询开始"&gt;&lt;/span&gt;
&lt;a href="#%e4%bb%8e%e6%85%a2%e6%9f%a5%e8%af%a2%e5%bc%80%e5%a7%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一切优化从定位问题开始。打开 MySQL 的慢查询日志（&lt;code&gt;slow_query_log&lt;/code&gt;），设置 &lt;code&gt;long_query_time = 0.1&lt;/code&gt;，跑一段时间后分析。PostgreSQL 用 &lt;code&gt;pg_stat_statements&lt;/code&gt; 扩展，同样有效。&lt;/p&gt;
&lt;p&gt;拿到慢查询，先 &lt;code&gt;EXPLAIN&lt;/code&gt; 看执行计划。关注几个关键字段：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;th&gt;目标&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;type&lt;/td&gt;
&lt;td&gt;访问类型&lt;/td&gt;
&lt;td&gt;至少到 range，最好到 ref/const&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;key&lt;/td&gt;
&lt;td&gt;使用的索引&lt;/td&gt;
&lt;td&gt;不为 NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rows&lt;/td&gt;
&lt;td&gt;扫描行数&lt;/td&gt;
&lt;td&gt;越小越好&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra&lt;/td&gt;
&lt;td&gt;额外信息&lt;/td&gt;
&lt;td&gt;避免 Using filesort / Using temporary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;最左前缀原则&lt;span class="hx:absolute hx:-mt-20" id="最左前缀原则"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%80%e5%b7%a6%e5%89%8d%e7%bc%80%e5%8e%9f%e5%88%99" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;联合索引是最容易出错的地方。假设索引是 &lt;code&gt;(a, b, c)&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WHERE a = 1&lt;/code&gt; ✅ 用到索引&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHERE a = 1 AND b = 2&lt;/code&gt; ✅ 用到索引&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHERE b = 2&lt;/code&gt; ❌ 用不到&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHERE a = 1 AND c = 3&lt;/code&gt; ⚠️ 只用到了 a 列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;排序也受这个规则约束。&lt;code&gt;ORDER BY a, b&lt;/code&gt; 没问题，&lt;code&gt;ORDER BY b, c&lt;/code&gt; 就需要额外排序。&lt;/p&gt;
&lt;h2&gt;覆盖索引&lt;span class="hx:absolute hx:-mt-20" id="覆盖索引"&gt;&lt;/span&gt;
&lt;a href="#%e8%a6%86%e7%9b%96%e7%b4%a2%e5%bc%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果查询需要的所有列都在索引里，就不需要回表取数据。&lt;code&gt;Extra&lt;/code&gt; 列显示 &lt;code&gt;Using index&lt;/code&gt; 就是覆盖索引。对一个高频查询，覆盖索引能减少大量随机 I/O。&lt;/p&gt;
&lt;p&gt;代价是索引变大了。不要为了覆盖索引把所有列都塞进索引——算一笔写入性能和存储空间的账。&lt;/p&gt;
&lt;h2&gt;真实案例&lt;span class="hx:absolute hx:-mt-20" id="真实案例"&gt;&lt;/span&gt;
&lt;a href="#%e7%9c%9f%e5%ae%9e%e6%a1%88%e4%be%8b" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;某个订单列表查询，原始 SQL 跑了 8 秒。&lt;code&gt;EXPLAIN&lt;/code&gt; 一看，&lt;code&gt;type: ALL&lt;/code&gt;，全表扫描 200 万行。加了 &lt;code&gt;(user_id, created_at)&lt;/code&gt; 联合索引后，&lt;code&gt;type: ref&lt;/code&gt;，扫描 20 行，耗时 12ms。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;索引不是越多越好&lt;/strong&gt;——每个索引都会拖慢写入速度并占用磁盘空间。建索引前问自己：这个查询的执行频率值得一个索引吗？&lt;/p&gt;</description></item><item><title>Svelte 初体验：不一样的思路</title><link>https://fukun.net/blog/svelte-first-look/</link><pubDate>Sun, 10 May 2026 09:30:00 +0800</pubDate><guid>https://fukun.net/blog/svelte-first-look/</guid><description>
&lt;p&gt;用惯了 React 和 Vue，第一次接触 Svelte 的时候确实有种新鲜感。最直观的感受：&lt;strong&gt;代码量少了很多&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;Runes 语法&lt;span class="hx:absolute hx:-mt-20" id="runes-语法"&gt;&lt;/span&gt;
&lt;a href="#runes-%e8%af%ad%e6%b3%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Svelte 5 引入了 runes 语法，让响应式声明更显式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;$state&lt;/code&gt;&lt;/strong&gt; — 声明响应式变量，替代了之前靠赋值触发响应式的隐式规则。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;$derived&lt;/code&gt;&lt;/strong&gt; — 衍生值，类似 Vue 的 computed。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;$effect&lt;/code&gt;&lt;/strong&gt; — 副作用，自动追踪依赖并在变化时执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-svelte" data-lang="svelte"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$derived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;$effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`count is now &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;逻辑清晰，没有 &lt;code&gt;useEffect&lt;/code&gt; 的依赖数组心智负担。&lt;/p&gt;
&lt;h2&gt;编译时魔法&lt;span class="hx:absolute hx:-mt-20" id="编译时魔法"&gt;&lt;/span&gt;
&lt;a href="#%e7%bc%96%e8%af%91%e6%97%b6%e9%ad%94%e6%b3%95" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Svelte 的核心理念：没有虚拟 DOM，编译器在构建时直接把组件转成高效的 DOM 操作代码。打包体积小得惊人，一个简单的组件可能只有几百字节。对移动端场景尤其友好。&lt;/p&gt;
&lt;h2&gt;SvelteKit&lt;span class="hx:absolute hx:-mt-20" id="sveltekit"&gt;&lt;/span&gt;
&lt;a href="#sveltekit" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;SvelteKit 是 Svelte 的全栈框架，文件路由设计简洁。约定优于配置——新建一个 &lt;code&gt;src/routes/about/+page.svelte&lt;/code&gt;，就自动有了 &lt;code&gt;/about&lt;/code&gt; 路由。内置 SSR、SSG、CSR 多种渲染模式，服务器端数据加载用 &lt;code&gt;load&lt;/code&gt; 函数，类型安全做得不错。&lt;/p&gt;
&lt;h2&gt;总结&lt;span class="hx:absolute hx:-mt-20" id="总结"&gt;&lt;/span&gt;
&lt;a href="#%e6%80%bb%e7%bb%93" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Svelte 适合想要减少样板代码、追求小打包体积的项目。React 和 Vue 的生态更成熟，但 Svelte 的思路值得关注。下一步打算用 SvelteKit 重写个人博客试试看。&lt;/p&gt;</description></item><item><title>SSH 隧道实用指南</title><link>https://fukun.net/blog/ssh-tunnel-guide/</link><pubDate>Sat, 09 May 2026 14:00:00 +0800</pubDate><guid>https://fukun.net/blog/ssh-tunnel-guide/</guid><description>
&lt;p&gt;SSH 隧道是开发者的必备技能，但很多人只用过最基本的 &lt;code&gt;ssh user@host&lt;/code&gt;。三种端口转发模式记清楚了，日常开发基本够用。&lt;/p&gt;
&lt;h2&gt;本地端口转发（-L）&lt;span class="hx:absolute hx:-mt-20" id="本地端口转发-l"&gt;&lt;/span&gt;
&lt;a href="#%e6%9c%ac%e5%9c%b0%e7%ab%af%e5%8f%a3%e8%bd%ac%e5%8f%91-l" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;把远程内网的服务映射到本机。&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -L 3306:db.internal:3306 jump-host&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这条命令让本地的 &lt;code&gt;localhost:3306&lt;/code&gt; 流量通过跳板机转发到内网的数据库服务器。典型场景：你在家开发，公司的数据库只在内网可访问。连上跳板机后，&lt;code&gt;mysql -h 127.0.0.1 -P 3306&lt;/code&gt; 就能直接操作数据库。&lt;/p&gt;
&lt;h2&gt;远程端口转发（-R）&lt;span class="hx:absolute hx:-mt-20" id="远程端口转发-r"&gt;&lt;/span&gt;
&lt;a href="#%e8%bf%9c%e7%a8%8b%e7%ab%af%e5%8f%a3%e8%bd%ac%e5%8f%91-r" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;反过来，把本地端口暴露给远程服务器。&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -R 8080:localhost:3000 public-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这个场景适合调试 webhook：你在本地起了服务，第三方服务需要回调你的端点。把本地的 3000 端口映射到公网服务器的 8080，webhook URL 填 &lt;code&gt;http://public-server:8080&lt;/code&gt; 就能收到回调。&lt;/p&gt;
&lt;p&gt;记得在 remote 服务器上设置 &lt;code&gt;GatewayPorts yes&lt;/code&gt; 才能让外部访问。&lt;/p&gt;
&lt;h2&gt;动态端口转发（-D）&lt;span class="hx:absolute hx:-mt-20" id="动态端口转发-d"&gt;&lt;/span&gt;
&lt;a href="#%e5%8a%a8%e6%80%81%e7%ab%af%e5%8f%a3%e8%bd%ac%e5%8f%91-d" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;临时搭建 SOCKS 代理。&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -D &lt;span class="m"&gt;1080&lt;/span&gt; jump-host&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="复制代码"
aria-label="复制代码"
data-copied-label="已复制！"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;然后浏览器或系统代理设置为 &lt;code&gt;socks5://localhost:1080&lt;/code&gt;，流量就全走跳板机出去了。配合 SwitchyOmega 这类浏览器扩展，按域名自动切换代理规则，体验更好。&lt;/p&gt;
&lt;h2&gt;实用技巧&lt;span class="hx:absolute hx:-mt-20" id="实用技巧"&gt;&lt;/span&gt;
&lt;a href="#%e5%ae%9e%e7%94%a8%e6%8a%80%e5%b7%a7" class="subheading-anchor" aria-label="此章节的永久链接"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;加 &lt;code&gt;-N&lt;/code&gt; 不执行远程命令，纯隧道。&lt;/li&gt;
&lt;li&gt;加 &lt;code&gt;-f&lt;/code&gt; 后台运行。&lt;/li&gt;
&lt;li&gt;加 &lt;code&gt;-C&lt;/code&gt; 压缩传输，慢网络下有用。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;~/.ssh/config&lt;/code&gt; 给常用隧道起别名，避免每次敲长命令。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三种模式记清楚，SSH 隧道基本够用。&lt;/p&gt;</description></item></channel></rss>