<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>SuzuhaYuki&apos;s Blog</title>
  <subtitle>Time goes by, and the years go by.</subtitle>
  <link href="https://blog.yuk1.uk/" rel="alternate" type="text/html"/>
  <link href="https://blog.yuk1.uk/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://blog.yuk1.uk/</id>
  <updated>2026-11-03T00:00:00.000Z</updated>
  <entry>
    <title>更新日志与友链申请事项</title>
    <link href="https://blog.yuk1.uk/posts/update-log-link-exchange-req/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/update-log-link-exchange-req/</id>
    <published>2024-12-25T00:00:00.000Z</published>
    <updated>2026-11-03T00:00:00.000Z</updated>
    <summary>更新日志、友链申请说明</summary>
    <content type="html"><![CDATA[<h1>更新日志与友链申请</h1>
<h2>友情链接申请说明</h2>
<p>请参照以下格式在本文章下评论，我会在审核通过后加入友链。</p>
<pre><code>{
  title: "SuzuhaYuki's Blog",
  imgurl: "https://yuk1.uk/assets/img/favicon.webp",
  desc: "Time goes by, and the years go by.",
  siteurl: "https://blog.yuk1.uk/",
  tags: ["Blog"]
}
</code></pre>
<h2>更新日志</h2>
<table>
<thead>
<tr>
<th>日期</th>
<th>更新类型</th>
<th>详细内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>2026-11-03</td>
<td>主题迁移</td>
<td>由 <a href="https://github.com/LyraVoid/Mizuki">Mizuki</a> 迁移至 <a href="https://github.com/LyraVoid/Shirone">Shirone</a></td>
</tr>
<tr>
<td>2026-05-23</td>
<td>平台迁移</td>
<td>将博客部署平台由传统服务器迁移至 <a href="https://vercel.com/">Vercel</a> Serverless 平台</td>
</tr>
<tr>
<td>2026-03-01</td>
<td>图片迁移</td>
<td>将图片由自建图床迁移至本地</td>
</tr>
<tr>
<td>2025-12-01</td>
<td>域名变更</td>
<td>从 <code>szh.rainettle.top</code> 更换为 <code>blog.yuk1.uk</code></td>
</tr>
<tr>
<td>2025-09-13</td>
<td>框架迁移</td>
<td>博客系统从 <a href="https://wordpress.org/download/">WordPress</a> 迁移至基于 <a href="https://astro.build/">Astro</a> 构建的 <a href="https://github.com/LyraVoid/Mizuki">Mizuki</a> 主题</td>
</tr>
<tr>
<td>2025-09-06</td>
<td>主题更新</td>
<td>主题由 <a href="https://github.com/baomihuahua/lolimeow">LoliMeow</a> 切换为 <a href="https://github.com/matsuzaka-yuki/Kizumi">Kizumi</a>，并将主题默认字体修改为 <a href="https://github.com/lxgw/LxgwWenkaiGB">霞鹜文楷 GB</a></td>
</tr>
<tr>
<td>2025-07-15</td>
<td>服务器迁移</td>
<td>因 <a href="https://nqidc.cn/">泥鳅云</a> 服务终止，将服务器迁移至 <a href="https://moebun.com/aff/FHUEXJAB">小兔互联</a></td>
</tr>
<tr>
<td>2025-04-24</td>
<td>服务器迁移</td>
<td>由 <a href="https://www.lyvps.net/">林柚云</a> 迁移至 <a href="https://nqidc.cn/">泥鳅云</a></td>
</tr>
<tr>
<td>2024-12-25</td>
<td>域名变更</td>
<td>从 <code>www.727252.xyz</code> 更换为 <code>szh.rainettle.top</code></td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Daily"/>
  </entry>
  <entry>
    <title>你的PR模板checkbox写错了</title>
    <link href="https://blog.yuk1.uk/posts/pr-checkbox-format/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/pr-checkbox-format/</id>
    <published>2026-05-08T00:00:00.000Z</published>
    <updated>2026-05-08T00:00:00.000Z</updated>
    <summary>那个看着像勾选框的[✓]，其实只是段纯文本。点不了、CI不认、字体还可能乱码。</summary>
    <content type="html"><![CDATA[<h1>你的 PR 模板 checkbox 写错了</h1>
<p><img src="./post-img/Snipaste_2026-05-08_00-10-25.webp" alt="不规范写法 w-100%" title="不规范写法" />
见过这种 PR 模板没？checklist 那一栏，勾选项是 <code>• [✓]</code>。<br />
这不是 checkbox。这就是个列表项后面跟了个带括号的 ✓，纯文本，点都点不动。<br />
正确的：
<img src="./post-img/Snipaste_2026-05-08_00-10-55.webp" alt="规范渲染效果 w-100%" title="规范渲染效果" />
<img src="./post-img/Snipaste_2026-05-08_00-11-20.webp" alt="规范源码 w-100%" title="规范源码" /></p>
<pre><code>- [x] Bug fix (a non-breaking change that fixes an issue)
- [ ] New feature (a non-breaking change that adds functionality)
- [ ] Breaking change
- [ ] Other (please describe):

## Checklist
- [x] I have read the [CONTRIBUTING](https://github.com/saicaca/fuwari/blob/main/CONTRIBUTING.md) document.
- [x] I have checked to ensure that this Pull Request is not for personal changes.
- [x] I have performed a self-review of my own code.
- [x] My changes generate no new warnings.
</code></pre>
<p>GFM task list 就一行：<code>- [x]</code> 已完成，<code>- [ ]</code> 未完成。渲染出来是真正的 checkbox，能点。<br />
用 <code>[✓]</code> 的话，贡献者打勾得手动编辑文本，CI bot 也当普通文字跳过。<code>✓</code> 在不同系统上长得还不一样，缺字体变方块我都见过。模板里这么写，新来的照着抄，后面到处都是这种东西。</p>
<pre><code>## Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Other:

## Checklist
- [ ] I have read the CONTRIBUTING document.
- [ ] This PR is not for personal changes.
- [ ] I have performed a self-review of my code.
- [ ] My changes generate no new warnings.
</code></pre>
<p>默认 <code>- [ ]</code>，别帮人预勾。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Knowledge"/>
  </entry>
  <entry>
    <title>拒绝技术造神，重识理性认知</title>
    <link href="https://blog.yuk1.uk/posts/rational-cognition-ai/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/rational-cognition-ai/</id>
    <published>2026-03-28T00:00:00.000Z</published>
    <updated>2026-03-28T00:00:00.000Z</updated>
    <summary>在 AI 狂热的浪潮中，我们应当拨开喧嚣的迷雾，回归技术本质，警惕那些利用焦虑收割韭菜的&quot;生意经&quot;。</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>每隔几年，科技圈就会经历一次集体性的情绪过山车——从元宇宙到 Web 3，从区块链到如今的 AI Agent，轨迹如出一辙：萌芽期被冷落，爆发期被追捧，泡沫期被神化，然后逐渐回归常态。<br />
AI 目前正处于这条曲线的峰值附近。朋友圈里“AI 即将取代 XX 职业”的论调此起彼伏，各类速成课程如雨后春笋般冒出。<br />
<strong>技术本身从不骗人，骗人的永远是围绕技术编织的叙事。</strong></p>
<h1>AI 到底能做什么、不能做什么？</h1>
<p>与其被动地被焦虑裹挟，不如先花十分钟搞清楚一件事情：这项技术在你所处的领域里，真实的效能边界在哪里。</p>
<h2>它擅长的：结构化、高频次的重复任务</h2>
<p>AI 的优势非常具体且可量化：</p>
<ul>
<li><strong>信息检索与整合</strong>——在庞杂的文档中快速定位关键段落，生成结构化摘要。</li>
<li><strong>模板化产出</strong>——根据明确的格式要求，批量生成文案初稿、代码脚手架、数据报表。</li>
<li><strong>语言翻译与润色</strong>——在语义准确度要求不极端的场景下，替代大部分人工翻译和校对工作。</li>
<li><strong>辅助学习</strong>——作为“苏格拉底式”的对话伙伴，帮助梳理知识框架、模拟面试问答。</li>
</ul>
<p>这些能力的共同特征是：<strong>目标明确、评价标准清晰、容错空间较大</strong>。在这个区间内，AI 确实能带来数倍于人工的效率提升。</p>
<h2>它做不好的：模糊决策与跨域推理</h2>
<p>然而，一旦任务脱离了上述“安全区”，AI 的表现就会急剧下降：</p>
<ol>
<li><strong>因果推理的薄弱环节</strong>：AI 擅长识别相关性，但不擅长建立因果链。当你问“为什么销售额下降了 20%”，它可以列出二十个可能的相关因素，但很难自主判断哪一个是根因——这需要结合行业经验和业务上下文。</li>
<li><strong>上下文断裂</strong>：在长周期、多步骤的复杂项目中，AI 会丢失早期设定的目标约束。你可能花了半小时跟它对齐需求，它转头就“忘了”，按照自己的理解跑偏。</li>
<li><strong>安全边界的盲区</strong>：AI 没有风险感知能力。它不会主动判断一条命令是否危险、一个建议是否合规，只会按照训练分布输出“最可能正确”的答案。在涉及数据安全、金融决策、医疗建议等高风险场景中，盲目信任 AI 的输出后果不堪设想。</li>
</ol>
<h1>为什么我们总是容易被”技术叙事”牵着走？</h1>
<p>问题不在于 AI 本身，而在于我们面对新技术时的认知模式。
心理学中有一个概念叫 <strong>“可得性启发”（Availability Heuristic）</strong>：人们倾向于根据最容易回想起来的案例来评估一件事情的概率和重要性。当你每天刷到的都是“AI 又取得了突破性进展”的新闻，你自然会高估 AI 当前的能力成熟度，同时低估它落地的真实门槛。<br />
这种认知偏差催生了一条完整的产业链：</p>
<ul>
<li>用极端案例制造紧迫感（“某某行业已被 AI 全面替代”）；</li>
<li>用简化话术降低心理门槛（“零基础，三天掌握 AI”）；</li>
<li>用社会认同强化从众行为（“已有 10 万人加入学习”）。</li>
</ul>
<p>这条链路的本质不是教育，而是<strong>信息差变现</strong>。真正掌握 AI 应用能力的人，往往在安静地解决具体问题，而不是站在台上卖门票。</p>
<h1>把 AI 用好的三个务实建议</h1>
<p>与其在“拥抱还是抗拒”的二元对立中内耗，不如把精力花在更有价值的事情上。</p>
<h2>1. 从具体痛点出发，而非从技术出发</h2>
<p>不要问“AI 能帮我做什么”，而要问“我手头哪个环节最耗时、最机械、最容易被标准化”。找到它，然后用 AI 去替换那个具体的环节。这才是技术落地的正确姿势——<strong>问题驱动，而非技术驱动。</strong></p>
<h2>2. 建立输出质量的人为防线</h2>
<p>AI 的输出质量高度依赖输入质量。在关键场景中，你需要做到：</p>
<ul>
<li><strong>提示词层面</strong>：明确角色设定、任务约束、输出格式和评价标准，避免模糊指令导致结果失控。</li>
<li><strong>结果验证层面</strong>：对 AI 交付的成果建立审核清单，尤其是涉及事实陈述、数据引用和逻辑推理的部分，逐一核验后再投入使用。</li>
<li><strong>迭代优化层面</strong>：当输出不达预期时，不要直接放弃或全盘接受，而是通过调整提示词、补充上下文、拆分子任务等手段逐步逼近目标。</li>
</ul>
<h2>3. 把省下来的时间投入到 AI 无法替代的事情上</h2>
<p>AI 善于处理确定性任务，但以下能力在可预见的未来仍然属于人类的专属领地：</p>
<ul>
<li><strong>定义正确的问题</strong>——AI 能高效解题，但判断“这道题值不值得解”的是人。</li>
<li><strong>跨学科整合</strong>——将技术方案与商业判断、用户洞察、伦理考量融合为一个完整的决策。</li>
<li><strong>承担责任</strong>——AI 可以生成建议，但最终为结果买单的永远是决策者本人。</li>
</ul>
<h1>结语</h1>
<p>每一轮技术浪潮最终都会沉淀出真正有价值的东西。蒸汽机没有让人类变成机器的附庸，互联网也没有让人类失去社交的能力。AI 同样如此。<br />
不必急于站队，也不必焦虑于某个截止日期。<strong>找到你自己工作中的那个“值得自动化的环节”，把它交给 AI，然后把省下来的时间花在更重要的事情上——这才是务实的态度。</strong><br />
至于那些喊着“不学就出局”的声音，不妨想想：一个真正成熟的技术，从来不需要靠恐吓来获取用户。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Technology"/>
  </entry>
  <entry>
    <title>使用Docker部署Misskey实例</title>
    <link href="https://blog.yuk1.uk/posts/misskey-docker/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/misskey-docker/</id>
    <published>2026-01-16T00:00:00.000Z</published>
    <updated>2026-01-16T00:00:00.000Z</updated>
    <summary>使用Docker部署Misskey去中心化社交网络实例的详细教程</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>Misskey 是一个开源的、去中心化的社交网络平台，它提供了丰富的功能和高度的自定义性，让用户能够自由地构建和管理自己的社交空间。与传统社交媒体不同，Misskey 支持联邦宇宙（Fediverse）协议，允许不同实例之间的用户相互交流，同时保持数据的控制权和隐私性。</p>
<h1>开始部署你的 Misskey 实例</h1>
<h2>拉取代码</h2>
<pre><code>git clone -b master https://github.com/misskey-dev/misskey.git
cd misskey
</code></pre>
<h2>复制配置文件示例</h2>
<pre><code>cp .config/docker_example.yml .config/default.yml
cp .config/docker_example.env .config/docker.env
cp ./compose_example.yml ./compose.yml
</code></pre>
<h2>编辑 default.yml</h2>
<p><code>default.yml</code> 是 Misskey 的核心配置文件。以下列出需要关注的关键配置项：</p>
<pre><code>url: https://example.tld/       # 必改：替换为你的实际域名

port: 3000                      # Misskey 监听端口，通常无需修改

db:
  host: db                     # 数据库主机名
  port: 5432                   # 数据库端口号
  db: misskey                   # 数据库名称
  user: example-misskey-user    # 数据库用户名
  pass: example-misskey-pass    # 数据库密码

dbReplications: false # 数据库复制，建议关闭

redis:
  host: redis                     # Redis 主机名
  port: 6379                      # Redis 端口号

id: 'aidx'                      # ID 生成方式，启动后请勿更改

fulltextSearch:
  provider: sqlLike             # 全文搜索，可选 sqlLike / sqlPgroonga / meilisearch

proxyBypassHosts: # 反向代理绕过主机名，用于访问外部服务
  - api.deepl.com               # DeepL API 主机名
  - api-free.deepl.com         # DeepL API 主机名，用于免费翻译
  - www.recaptcha.net          # reCAPTCHA 主机名，用于验证码
  - hcaptcha.com               # hCaptcha 主机名，用于验证码
  - challenges.cloudflare.com # Cloudflare 主机名，用于验证码
</code></pre>
<blockquote>
<p><strong>注意事项：</strong></p>
<ul>
<li><code>url</code> 和 <code>id</code> 这两项一旦启动实例后<strong>不可更改</strong>，请务必在首次启动前确认正确。</li>
<li>更多可选配置（如 Sentry 错误追踪、Meilisearch、Redis 分离等）可参考 <a href="https://misskey-hub.net/docs/admin/install-resources/">Misskey 官方文档</a>，此处不一一展开。</li>
</ul>
</blockquote>
<h2>编辑 docker.env</h2>
<p><code>docker.env</code> 包含 PostgreSQL 的连接凭据，<strong>必须与 <code>default.yml</code> 中的 <code>db</code> 配置保持一致</strong>：</p>
<pre><code>POSTGRES_PASSWORD=example-misskey-pass
POSTGRES_USER=example-misskey-user
POSTGRES_DB=misskey
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
</code></pre>
<blockquote>
<p>如果修改了 <code>default.yml</code> 中的数据库用户名、密码或数据库名，请同步修改此处，否则 Misskey 将无法连接数据库。</p>
</blockquote>
<h2>编辑compose.yml</h2>
<p><code>compose.yml</code>文件定义了 Docker 容器的配置：</p>
<pre><code>services:
  web:
    build: .
    restart: always
    links:
      - db
      - redis
#     - mcaptcha
#     - meilisearch
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - "3000:3000" # 映射主机端口到容器端口
    networks:
      - internal_network
      - external_network
    # env_file:
    #   - .config/docker.env
    volumes:
      - ./files:/misskey/files
      - ./.config:/misskey/.config:ro

  redis:
    restart: always
    image: redis:7-alpine
    networks:
      - internal_network
    volumes:
      - ./redis:/data
    healthcheck:
      test: "redis-cli ping"
      interval: 5s
      retries: 20

  db:
    restart: always
    image: postgres:18-alpine
    networks:
      - internal_network
    env_file:
      - .config/docker.env
    volumes:
      - ./db:/var/lib/postgresql
    healthcheck:
      test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
      interval: 5s
      retries: 20

#  mcaptcha:
#    restart: always
#    image: mcaptcha/mcaptcha:latest
#    networks:
#      internal_network:
#      external_network:
#        aliases:
#          - localhost
#    ports:
#      - 7493:7493
#    env_file:
#      - .config/docker.env
#    environment:
#      PORT: 7493
#      MCAPTCHA_redis_URL: "redis://mcaptcha_redis/"
#    depends_on:
#      db:
#        condition: service_healthy
#      mcaptcha_redis:
#        condition: service_healthy
#
#  mcaptcha_redis:
#    image: mcaptcha/cache:latest
#    networks:
#      - internal_network
#    healthcheck:
#      test: "redis-cli ping"
#      interval: 5s
#      retries: 20

#  meilisearch:
#    restart: always
#    image: getmeili/meilisearch:v1.3.4
#    environment:
#      - MEILI_NO_ANALYTICS=true
#      - MEILI_ENV=production
#    env_file:
#      - .config/meilisearch.env
#    networks:
#      - internal_network
#    volumes:
#      - ./meili_data:/meili_data

networks:
  internal_network:
    internal: true
  external_network:

</code></pre>
<h2>构建镜像与初始化数据库</h2>
<p>使用以下命令进行Misskey的构建和数据库初始化。这可能需要一些时间。</p>
<pre><code>docker compose build                # 构建 Docker 镜像（首次较慢）
docker compose run --rm web pnpm run init  # 初始化数据库
</code></pre>
<h2>启动Misskey实例</h2>
<p>完成构建和初始化后，使用以下命令启动Misskey容器：</p>
<pre><code>docker compose up -d
</code></pre>
<h2>故障排除</h2>
<h3>INTERNAL_ERROR错误处理</h3>
<p>访问实例时出现 <code>Misskey Internal error occurred</code>，通常是文件权限问题：</p>
<pre><code>chmod -R 777 ./files
docker compose restart web
</code></pre>
<h2>更新Misskey</h2>
<p>当有新版本发布时，可以使用以下步骤更新Misskey实例：</p>
<pre><code>git stash # 保存当前工作目录的更改
git checkout master # 切换到master分支
git pull # 拉取最新代码
git submodule update --init # 更新子模块
git stash pop # 恢复之前的更改
docker compose build # 构建新镜像
docker compose stop &amp;&amp; docker compose up -d # 重启容器
</code></pre>
<h1>结语</h1>
<p>通过本教程，你已经完成了 Misskey 实例的部署。日常维护中，定期执行更新命令并关注日志输出，可以确保实例的稳定运行。<br />
如需进一步配置（如对象存储、邮件发送、防垃圾措施等），请参考 <a href="https://misskey-hub.net/">Misskey Hub 官方文档</a>。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>2025年度总结</title>
    <link href="https://blog.yuk1.uk/posts/2025-year-in-review/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/2025-year-in-review/</id>
    <published>2026-01-08T00:00:00.000Z</published>
    <updated>2026-01-08T00:00:00.000Z</updated>
    <summary>2025年度回顾：代码、生活与热爱</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>2025年转瞬即逝。回望这一年，有代码提交的喜悦，有设备更替的感慨，也有深夜烧烤与烟花下的青春印记。这篇小记，既是对过往的梳理，也是给未来自己的一个温柔提醒：无论技术如何迭代、生活如何流转，热爱与好奇始终是前行的灯。</p>
<h2>Web &amp; Coding</h2>
<p>今年9月，我初次接触基于 Astro 构建的 <strong>Mizuki</strong> 项目，随即决定将博客从 WordPress 迁移至此。<br />
同年12月，我提交了人生第一个 Pull Request（PR），修复了上游仓库的样式问题，并解决了项目的多个 hint。<br />
此外，今年我还结识了一位新“朋友” —— <strong>GLM-4.7</strong>，它在许多方面给予了我极大帮助。</p>
<h2>Study &amp; Life</h2>
<p>学业方面依旧如常。9月，我升入大二；然而开学不到一周，陪伴我许久的 <strong>Redmi Note 10 Pro（8GB+256GB）</strong> 光荣退役，迎来了新伙伴 <strong>Redmi Turbo 4 Pro（16GB+1TB）</strong>。<br />
也是从9月起，我与 Mizuki 项目的开发者逐渐成为网络上的挚友。<br />
元旦前夕，我和八位同学一起烧烤聚餐，再次欣赏了跨年夜的烟花；但后半夜，我们转战网咖，通宵奋战至清晨五点。</p>
<h2>Anime &amp; Movie</h2>
<p>秋季学期开始后，我补完了《间谍过家家》第一季下半部分，以及《街角魔族 二丁目》《摇滚乃是淑女的嗜好》《幸运星》等番剧。<br />
今年还去影院看了两部电影：《我的世界大电影》和《捕风追影》。</p>
<h1>结语</h1>
<p>站在2026年的起点回望，那些调试到凌晨的终端、新手机开箱的雀跃、番剧片尾曲响起时的满足，都成了时光里闪亮的碎片。愿新的一年，继续在代码与烟火气之间，找到属于自己的节奏——稳稳地，向前走。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Daily"/>
  </entry>
  <entry>
    <title>Windows下的ADB配置指南</title>
    <link href="https://blog.yuk1.uk/posts/adb-setup-on-windows/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/adb-setup-on-windows/</id>
    <published>2025-12-28T00:00:00.000Z</published>
    <updated>2025-12-28T00:00:00.000Z</updated>
    <summary>详细介绍如何在Windows系统上配置ADB，包括下载SDK Platform-Tools、设置环境变量和验证配置</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>Android 调试桥（ADB，Android Debug Bridge）是一款功能强大的命令行工具，用于与 Android 设备进行通信。通过 <code>adb</code> 命令，您可以执行多种设备操作，如安装和调试应用，还可以访问设备上的 Unix Shell，直接在设备中运行各类命令。<br />
ADB 采用客户端-服务器架构，由三个核心组件构成：</p>
<ul>
<li><strong>客户端（Client）</strong>：运行在开发主机上，负责发送命令。用户可通过终端输入 <code>adb</code> 命令来调用该客户端。</li>
<li><strong>守护进程（adbd）</strong>：运行在设备上，负责执行命令。该守护进程以后台服务的形式运行于每一台连接的 Android 设备中。</li>
<li><strong>服务器（Server）</strong>：运行在开发主机上，负责管理客户端与设备端守护进程之间的通信，并在后台持续监听来自客户端的请求。</li>
</ul>
<h1>正式开启</h1>
<h2>1. 下载 SDK Platform-Tools</h2>
<p>访问 <a href="https://developer.android.google.cn/tools/releases/platform-tools?hl=zh-cn#downloads">SDK 平台工具版本说明</a> 页面，点击 <code>下载内容</code> 下方的 <code>下载适用于 Windows 的 SDK Platform-Tools</code> 按钮。在弹出的窗口中勾选 <code>我已同意上述条款及条件</code>，即可开始下载。</p>
<h2>2. 解压与配置环境变量</h2>
<ol>
<li>下载完成后，您将得到一个名为 <code>platform-tools-latest-windows.zip</code> 的压缩包。将这个压缩包解压到任意位置。</li>
<li>使用 Windows 搜索功能，输入 <code>环境变量</code>，然后选择 <code>编辑系统环境变量</code>。
<img src="./post-img/953756.webp" alt="953756 w-100%" title="编辑系统环境变量" /></li>
<li>点击 <code>环境变量(N)...</code>，在 <code>XXX 的用户变量(U)</code> 区域找到 <code>Path</code> 变量，点击 <code>编辑</code>。</li>
<li>在编辑窗口中，点击 <code>新建(N)</code>，然后输入 <code>platform-tools</code> 文件夹的完整路径。例如： <code>C:\Downloads\platform-tools-latest-windows\platform-tools</code>。</li>
</ol>
<h2>3. 验证配置是否成功</h2>
<p>配置完成后，按 <code>Win + R</code>，输入 <code>cmd</code> 并回车，打开命令提示符窗口。在其中执行以下命令：</p>
<pre><code>adb version
</code></pre>
<p>如果正确显示 ADB 的版本信息（例如 <code>Android Debug Bridge version 1.0.41</code>），则说明环境变量配置成功，ADB 已可正常使用。
<img src="./post-img/181683.webp" alt="181683 w-100%" title="验证ADB配置是否成功" /></p>
<blockquote>
<p>如果提示 'adb' 不是内部或外部命令，也不是可运行的程序或批处理文件。<br />
请检查路径是否输入正确，并确保已重启命令行窗口。</p>
</blockquote>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>为什么Windows显示的硬盘容量比标称小？</title>
    <link href="https://blog.yuk1.uk/posts/storage-capacity-discrepancy/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/storage-capacity-discrepancy/</id>
    <published>2025-12-12T00:00:00.000Z</published>
    <updated>2025-12-12T00:00:00.000Z</updated>
    <summary>解析Windows显示硬盘容量比标称小的原因，以及十进制与二进制存储容量计算标准的差异</summary>
    <content type="html"><![CDATA[<h1>为什么Windows显示的硬盘容量比标称小？</h1>
<p>你是否曾经疑惑过，为什么购买的硬盘或U盘，在Windows系统中显示的实际可用容量总是小于厂商宣传的容量？这其实不是厂商的“欺诈”行为，而是由于存储设备厂商和操作系统采用了不同的容量计算标准。</p>
<h2>容量计算标准详解</h2>
<h3>1. 厂商使用的十进制标准</h3>
<p>硬盘、U盘等存储设备制造商采用国际单位制（SI）定义的十进制标准，以 1,000 为单位递进：</p>
<table>
<thead>
<tr>
<th>单位</th>
<th>换算关系</th>
<th>字节数</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 KB</td>
<td>= 1,000 B</td>
<td>$10^3$</td>
</tr>
<tr>
<td>1 MB</td>
<td>= 1,000 KB</td>
<td>$10^6$</td>
</tr>
<tr>
<td>1 GB</td>
<td>= 1,000 MB</td>
<td>$10^9$</td>
</tr>
<tr>
<td>1 TB</td>
<td>= 1,000 GB</td>
<td>$10^{12}$</td>
</tr>
</tbody>
</table>
<p>因此，一块<strong>标称1TB</strong>的硬盘，其物理容量就是实打实的 <strong>1,000,000,000,000</strong> 字节，即 $10^{12}$ 字节。</p>
<h3>2. Windows使用的二进制标准</h3>
<p>计算机底层基于二进制运行，操作系统和软件天然以 1,024（即 $2^{10}$）为基数管理存储空间。国际电工委员会（IEC）为这套标准定义了独立的单位名称：</p>
<table>
<thead>
<tr>
<th>单位</th>
<th>换算关系</th>
<th>字节数</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 KiB</td>
<td>= 1,024 B</td>
<td>$2^{10}$</td>
</tr>
<tr>
<td>1 MiB</td>
<td>= 1,024 KiB</td>
<td>$2^{20}$</td>
</tr>
<tr>
<td>1 GiB</td>
<td>= 1,024 MiB</td>
<td>$2^{30}$</td>
</tr>
<tr>
<td>1 TiB</td>
<td>= 1,024 GiB</td>
<td>$2^{40}$</td>
</tr>
</tbody>
</table>
<p><strong>关键在于</strong>：Windows 实际上使用的是二进制计算（除以 1,024），却在界面上标注为 "GB" 而非规范的 "GiB"。</p>
<h2>容量差异从何而来</h2>
<p>十进制和二进制之间的换算系数为：
$$\frac{1,000^3}{1,024^3} = \frac{1,000,000,000}{1,073,741,824} \approx 0.9313$$
也就是说，<strong>厂商的 1 GB 在 Windows 中只显示约 0.93 GB</strong>，差异约 6.87%。容量越大，这个差异的绝对值越明显。</p>
<h3>实际案例</h3>
<p>以常见的几种硬盘规格为例：</p>
<table>
<thead>
<tr>
<th>厂商标称</th>
<th>物理字节数</th>
<th>Windows 显示</th>
<th>“缩水”量</th>
</tr>
</thead>
<tbody>
<tr>
<td>128 GB</td>
<td>128,000,000,000</td>
<td>≈ 119.2 GB</td>
<td>8.8 GB</td>
</tr>
<tr>
<td>256 GB</td>
<td>256,000,000,000</td>
<td>≈ 238.4 GB</td>
<td>17.6 GB</td>
</tr>
<tr>
<td>512 GB</td>
<td>512,000,000,000</td>
<td>≈ 476.8 GB</td>
<td>35.2 GB</td>
</tr>
<tr>
<td>1 TB</td>
<td>1,000,000,000,000</td>
<td>≈ 931.3 GB</td>
<td>68.7 GB</td>
</tr>
<tr>
<td>2 TB</td>
<td>2,000,000,000,000</td>
<td>≈ 1,862.6 GB</td>
<td>137.4 GB</td>
</tr>
</tbody>
</table>
<blockquote>
<p>一个实用的速算技巧：将厂商标称容量乘以 0.931，即可快速估算 Windows 中的显示容量。</p>
</blockquote>
<h2>为什么内存不受影响？</h2>
<p>你可能注意到，电脑内存的容量几乎总是精确的——标称 8 GB 就是 8 GB。这是因为：</p>
<ul>
<li>内存行业从<strong>设计之初</strong>就直接采用了二进制标准</li>
<li>一条标称<strong>8 GB</strong>的内存，其容量就是 $8 \times 2^{30}$ = <strong>8,589,934,592</strong> 字节</li>
<li>操作系统同样使用二进制标准识别内存，标准一致，自然不会出现显示差异</li>
</ul>
<h2>其他操作系统的做法</h2>
<p>并非所有系统都像 Windows 这“混合标注”：</p>
<ul>
<li><strong>macOS</strong>（10.6 Snow Leopard 起）和多数 <strong>Linux 发行版</strong>已直接使用十进制（SI）标准显示存储容量</li>
<li>在这些系统上，1 TB 硬盘显示的容量接近 1,000 GB，与厂商标称值一致</li>
<li>这种做法遵循了 SI 国际单位制的定义，避免了换算混淆</li>
</ul>
<h2>如何准确估算可用容量</h2>
<p>购买硬盘时，可以用以下公式快速估算 Windows 中的实际显示容量：</p>
<p>$$\text{Windows 容量} \approx \text{厂商标称} \times 0.931$$</p>
<p>此外还需注意，硬盘实际可用空间还会因以下因素进一步减少：</p>
<ul>
<li><strong>文件系统开销</strong>：格式化（NTFS、FAT32 等）本身会占用一部分空间用于存储文件分配表和元数据</li>
<li><strong>保留空间</strong>：部分 SSD 会预留空间用于磨损均衡和坏块替换，通常为标称容量的 7% 左右</li>
<li><strong>恢复分区</strong>：部分厂商预装的恢复分区也会占用数十 GB 的空间</li>
</ul>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Knowledge"/>
  </entry>
  <entry>
    <title>词频统计与Federation机制的实现</title>
    <link href="https://blog.yuk1.uk/posts/wordcount-and-federation/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/wordcount-and-federation/</id>
    <published>2025-12-09T00:00:00.000Z</published>
    <updated>2025-12-09T00:00:00.000Z</updated>
    <summary>Hadoop词频统计与Federation机制实现指南</summary>
    <content type="html"><![CDATA[<h1>词频统计</h1>
<ol>
<li>在 HDFS 中创建输入目录：<pre><code>hdfs dfs -mkdir -p /wordcount/input
</code></pre>
</li>
<li>在 <code>hadoop1</code> 节点本地创建 <code>word.txt</code> 文件并写入测试文本，然后上传至 HDFS：<pre><code>hdfs dfs -put ./word.txt /wordcount/input
</code></pre>
</li>
<li>进入 MapReduce 示例 JAR 所在目录，运行 WordCount 作业：<pre><code>cd /export/servers/hadoop-3.1.3/share/hadoop/mapreduce/
hadoop jar hadoop-mapreduce-examples-3.1.3.jar wordcount /wordcount/input /wordcount/output
</code></pre>
</li>
</ol>
<blockquote>
<p><strong>注意</strong>：输出路径 <code>/wordcount/output</code> 默认应为不存在，否则将操作失败</p>
</blockquote>
<h1>Federation 机制配置</h1>
<p>参考：NameNode和SecondaryNameNode部署在hadoop2和hadoop3上</p>
<h2>1. 停止现有 Hadoop 服务</h2>
<p>在 <code>hadoop1</code> 上依次执行以下命令，关闭 YARN 和 HDFS：</p>
<pre><code>stop-yarn.sh
stop-dfs.sh
</code></pre>
<h2>2. 清理旧数据</h2>
<p>在 <code>hadoop1</code>、<code>hadoop2</code> 和 <code>hadoop3</code> 上删除 HDFS 数据目录（根据实际配置路径）：</p>
<pre><code>rm -rf /export/data/hadoop-wfb-3.1.3/*
</code></pre>
<h2>3. 修改 <code>hdfs-site.xml</code> 配置文件</h2>
<p>在 <code>hadoop1</code> 上编辑 Hadoop 配置文件：</p>
<pre><code>vi /export/servers/hadoop-3.1.3/etc/hadoop/hdfs-site.xml
</code></pre>
<p>替换为以下支持 Federation 的配置：</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;?xml-stylesheet type="text/xsl" href="configuration.xsl"?&gt;

&lt;configuration&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.replication&lt;/name&gt;
        &lt;value&gt;2&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.nameservices&lt;/name&gt;
        &lt;value&gt;ns1,ns2&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.rpc-address.ns1&lt;/name&gt;
        &lt;value&gt;hadoop2:9000&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.http-address.ns1&lt;/name&gt;
        &lt;value&gt;hadoop2:9870&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.secondary.http-address.ns1&lt;/name&gt;
        &lt;value&gt;hadoop2:9880&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.rpc-address.ns2&lt;/name&gt;
        &lt;value&gt;hadoop3:9000&lt;/value&gt;
    &lt;/property&gt;
    &lt;property&gt;
&lt;/configuration&gt;
</code></pre>
<h2>4. 分发配置文件</h2>
<p>将更新后的 <code>hdfs-site.xml</code> 同步到 <code>hadoop2</code> 和 <code>hadoop3</code>：</p>
<pre><code>scp /export/servers/hadoop-3.1.3/etc/hadoop/hdfs-site.xml root@hadoop2:/export/servers/hadoop-3.1.3/etc/hadoop/
scp /export/servers/hadoop-3.1.3/etc/hadoop/hdfs-site.xml root@hadoop3:/export/servers/hadoop-3.1.3/etc/hadoop/
</code></pre>
<h2>5. 格式化 NameNode（带统一 Cluster ID）</h2>
<p>在 <code>hadoop2</code> 上格式化第一个命名空间：</p>
<pre><code>hdfs namenode -format -clusterId fed1
</code></pre>
<p>在 <code>hadoop3</code> 上格式化第二个命名空间（<strong>必须使用相同的 <code>-clusterId</code></strong>）：</p>
<pre><code>hdfs namenode -format -clusterId fed1
</code></pre>
<blockquote>
<p><strong>关键说明</strong>：Federation 要求所有 NameNode 使用相同的 <code>clusterId</code>，否则无法共存于同一集群。</p>
</blockquote>
<h2>6. 启动 Hadoop 集群</h2>
<p>在 <code>hadoop2</code> 上启动 HDFS（YARN 仍由原 ResourceManager 节点管理，此处建议按实际架构调整）：</p>
<pre><code>start-dfs.sh
</code></pre>
<blockquote>
<p><strong>注意</strong>：
YARN 通常不需要随 Federation 改动。若 ResourceManager 仍在 <code>hadoop2</code>，可继续执行：</p>
<pre><code>start-yarn.sh
</code></pre>
</blockquote>
<h2>7. 验证进程</h2>
<p>在 <code>hadoop1</code>、<code>hadoop2</code>、<code>hadoop3</code> 上分别执行：</p>
<pre><code>jps
</code></pre>
<p>预期结果：</p>
<ul>
<li><strong>hadoop2</strong>：NameNode（ns1）、DataNode、SecondaryNameNode、ResoureceManager、NodeManager</li>
<li><strong>hadoop3</strong>：NameNode（ns2）、DataNode、NodeManager、SecondaryNameNode</li>
<li><strong>hadoop1</strong>：DataNode、NodeManager</li>
</ul>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>部署Hadoop完全分布式集群</title>
    <link href="https://blog.yuk1.uk/posts/hadoop-cluster-deployment-in-vms/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/hadoop-cluster-deployment-in-vms/</id>
    <published>2025-12-08T00:00:00.000Z</published>
    <updated>2025-12-08T00:00:00.000Z</updated>
    <summary>Hadoop 3.1.3完全分布式集群部署详细指南</summary>
    <content type="html"><![CDATA[<h1>环境</h1>
<p><strong>虚拟化平台</strong>：VMware Workstation Pro 17<br />
<strong>操作系统</strong>：CentOS 7<br />
<strong>Hadoop 版本</strong>：3.1.3<br />
<strong>Java 环境</strong>：OpenJDK 1.8.0</p>
<h1>正式开始</h1>
<h2>一、准备工作</h2>
<h3>1. 克隆虚拟机</h3>
<ol>
<li>启动 VMware Workstation Pro 17。</li>
<li>右键点击已配置好的 <code>hadoop1</code> 虚拟机，选择 <strong>“管理” &gt; “克隆”</strong>。</li>
<li>在克隆向导中选择 <strong>“完整克隆”</strong>，创建两台新虚拟机。</li>
<li>克隆完成后，将拥有三台虚拟机：<code>hadoop1</code>、<code>hadoop2</code> 和 <code>hadoop3</code>。</li>
</ol>
<h3>2. 配置 VMware 虚拟网络</h3>
<ol>
<li>点击顶部菜单栏 <strong>“编辑” &gt; “虚拟网络编辑器”</strong>。</li>
<li>选中 <strong>VMnet8（NAT 模式）</strong>，取消勾选 <strong>“使用本地 DHCP 服务将 IP 地址分配给虚拟机”</strong>。</li>
<li>设置子网 IP 为：<code>192.168.121.0</code>。</li>
<li>点击 <strong>“NAT 设置”</strong>，将网关 IP 设为：<code>192.168.121.2</code>。</li>
<li>点击 <strong>“应用”</strong> 保存配置。</li>
</ol>
<h2>二、配置网络与 hosts 文件</h2>
<h3>1. 配置静态 IP</h3>
<p>在每台虚拟机中执行以下操作：</p>
<p>编辑网络接口配置文件（假设网卡为 <code>ens33</code>）：</p>
<pre><code>vi /etc/sysconfig/network-scripts/ifcfg-ens33
</code></pre>
<p>修改或添加如下内容（以 <code>hadoop1</code> 为例）：</p>
<pre><code>BOOTPROTO=static
ONBOOT=yes
IPADDR=192.168.121.160
GATEWAY=192.168.121.2
DNS1=223.5.5.5
</code></pre>
<blockquote>
<p><strong>注意</strong>：<code>hadoop2</code> 和 <code>hadoop3</code> 分别使用 <code>.161</code> 和 <code>.162</code> 作为 <code>IPADDR</code>。</p>
</blockquote>
<p>保存后重启网络服务：</p>
<pre><code>systemctl restart network
</code></pre>
<h3>2. 设置主机名</h3>
<p>分别在三台机器上设置唯一主机名：</p>
<pre><code># hadoop1
hostnamectl set-hostname hadoop1

# hadoop2
hostnamectl set-hostname hadoop2

# hadoop3
hostnamectl set-hostname hadoop3
</code></pre>
<h3>3. 配置 <code>/etc/hosts</code></h3>
<p>在所有节点的 <code>/etc/hosts</code> 中添加以下内容：</p>
<pre><code>192.168.121.160 hadoop1
192.168.121.161 hadoop2
192.168.121.162 hadoop3
</code></pre>
<p>确保三台机器的 <code>hosts</code> 文件完全一致。</p>
<h2>三、配置 SSH 免密登录与安装基础环境</h2>
<h3>1. 生成 RSA 密钥</h3>
<p>在每台机器上执行（一路回车使用默认路径）：</p>
<pre><code>ssh-keygen -t rsa
</code></pre>
<h3>2. 配置免密登录</h3>
<p>以 <code>hadoop1</code> 为主控节点，执行以下命令将公钥分发到所有节点（包括自身）：</p>
<pre><code>ssh-copy-id hadoop1
ssh-copy-id hadoop2
ssh-copy-id hadoop3
</code></pre>
<blockquote>
<p>首次连接时会提示确认主机指纹，输入 <code>yes</code>，然后输入对应主机的 root 密码。
<strong>注意</strong>：每台机子都需配置免密登录</p>
</blockquote>
<h3>3. 安装 OpenJDK</h3>
<p>在所有节点安装含开发工具的 OpenJDK：</p>
<pre><code>yum install -y java-1.8.0-openjdk-devel.x86_64
</code></pre>
<p>验证安装：</p>
<pre><code>java -version
</code></pre>
<h3>4. 安装 Hadoop</h3>
<ol>
<li>安装文件上传工具：<pre><code>yum install -y lrzsz
</code></pre>
</li>
<li>使用 <code>rz</code> 命令上传本地 Hadoop 安装包（如 <code>hadoop-3.1.3.tar.gz</code>）。</li>
<li>解压至指定目录（例如 <code>/export/servers/</code>）：<pre><code>mkdir -p /export/servers
tar -xzvf hadoop-3.1.3.tar.gz -C /export/servers/
</code></pre>
</li>
</ol>
<h2>四、配置 Hadoop 环境</h2>
<h3>1. 配置全局环境变量</h3>
<p>编辑 <code>/etc/profile</code>，在文件末尾添加：</p>
<pre><code>export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
export HADOOP_HOME=/export/servers/hadoop-3.1.3
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
</code></pre>
<p>使配置生效：</p>
<pre><code>source /etc/profile
</code></pre>
<h3>2. 修改 Hadoop 配置文件</h3>
<p>进入配置目录：</p>
<pre><code>cd /export/servers/hadoop-3.1.3/etc/hadoop/
</code></pre>
<h4><strong>core-site.xml</strong></h4>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;?xml-stylesheet type="text/xsl" href="configuration.xsl"?&gt;

&lt;configuration&gt;
&lt;property&gt;
    &lt;name&gt;fs.defaultFS&lt;/name&gt;
    &lt;value&gt;hdfs://hadoop1:9000&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;hadoop.tmp.dir&lt;/name&gt;
    &lt;value&gt;/export/data/hadoop-wfb-3.1.3&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
   &lt;name&gt;hadoop.http.staticuser.user&lt;/name&gt;
   &lt;value&gt;root&lt;/value&gt;
&lt;/property&gt;
&lt;/configuration&gt;
</code></pre>
<h4><strong>hdfs-site.xml</strong></h4>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;?xml-stylesheet type="text/xsl" href="configuration.xsl"?&gt;

&lt;configuration&gt;
&lt;!-- nn web端访问地址--&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.http-address&lt;/name&gt;
        &lt;value&gt;hadoop1:9870&lt;/value&gt;
    &lt;/property&gt;
	&lt;!-- 2nn web端访问地址--&gt;
    &lt;property&gt;
        &lt;name&gt;dfs.namenode.secondary.http-address&lt;/name&gt;
        &lt;value&gt;hadoop3:9868&lt;/value&gt;
    &lt;/property&gt;
&lt;/configuration&gt;
</code></pre>
<h4><strong>hadoop-env.sh</strong></h4>
<pre><code>export HADOOP_OS_TYPE=${HADOOP_OS_TYPE:-$(uname -s)}
export JAVA_HOME=/export/servers/jdk1.8.0_212
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root
</code></pre>
<h4><strong>mapred-site.xml</strong></h4>
<pre><code>&lt;?xml version="1.0"?&gt;
&lt;?xml-stylesheet type="text/xsl" href="configuration.xsl"?&gt;

&lt;configuration&gt;
    &lt;property&gt;
        &lt;name&gt;mapreduce.framework.name&lt;/name&gt;
        &lt;value&gt;yarn&lt;/value&gt;
    &lt;/property&gt;
&lt;/configuration&gt;
</code></pre>
<h4><strong>yarn-site.xml</strong></h4>
<pre><code>&lt;?xml version="1.0"?&gt;

&lt;configuration&gt;
&lt;!-- Site specific YARN configuration properties --&gt;
&lt;!-- 指定MR走shuffle --&gt;
    &lt;property&gt;
        &lt;name&gt;yarn.nodemanager.aux-services&lt;/name&gt;
        &lt;value&gt;mapreduce_shuffle&lt;/value&gt;
    &lt;/property&gt;

    &lt;!-- 指定ResourceManager的地址--&gt;
    &lt;property&gt;
        &lt;name&gt;yarn.resourcemanager.hostname&lt;/name&gt;
        &lt;value&gt;hadoop2&lt;/value&gt;
    &lt;/property&gt;
    &lt;!-- 环境变量的继承 --&gt;
    &lt;property&gt;
        &lt;name&gt;yarn.nodemanager.env-whitelist&lt;/name&gt;&lt;value&gt;JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME&lt;/value&gt;
    &lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;yarn.resourcemanager.webapp.address&lt;/name&gt;
    &lt;value&gt;hadoop2:8088&lt;/value&gt; &lt;!-- ResourceManager WebUI端口 --&gt;
&lt;/property&gt;
  &lt;property&gt;
    &lt;name&gt;yarn.resourcemanager.address&lt;/name&gt;
    &lt;value&gt;hadoop2:8032&lt;/value&gt; &lt;!-- 资源管理器地址 --&gt;
&lt;/property&gt;
  &lt;property&gt;
    &lt;name&gt;yarn.resourcemanager.scheduler.address&lt;/name&gt;
    &lt;value&gt;hadoop2:8030&lt;/value&gt; &lt;!-- 调度器地址 --&gt;
&lt;/property&gt;
&lt;/configuration&gt;
</code></pre>
<h4><strong>workers</strong></h4>
<pre><code>hadoop1
hadoop2
hadoop3
</code></pre>
<h3>3. 分发配置</h3>
<p>将 Hadoop 安装目录和 <code>/etc/profile</code> 配置同步到其他节点（以 <code>hadoop1</code> 为源）：</p>
<pre><code>scp -r /export/servers/hadoop-3.1.3 root@hadoop2:/export/servers/
scp -r /export/servers/hadoop-3.1.3 root@hadoop3:/export/servers/

scp /etc/profile root@hadoop2:/etc/
scp /etc/profile root@hadoop3:/etc/
</code></pre>
<p>在 <code>hadoop2</code> 和 <code>hadoop3</code> 上执行：</p>
<pre><code>source /etc/profile
</code></pre>
<h3>4. 初始化与启动集群</h3>
<h4>格式化 HDFS（仅在 hadoop1 执行一次）</h4>
<pre><code>hdfs namenode -format
</code></pre>
<h4>启动 HDFS（在 hadoop1）</h4>
<pre><code>start-dfs.sh
</code></pre>
<h4>启动 YARN（在 hadoop2）</h4>
<pre><code>start-yarn.sh
</code></pre>
<h3>验证进程</h3>
<p>在各节点运行 <code>jps</code>，预期输出：</p>
<ul>
<li><strong>hadoop1</strong>：NameNode、DataNode、NodeManager</li>
<li><strong>hadoop2</strong>：ResourceManager、NodeManager、DataNode</li>
<li><strong>hadoop3</strong>：NodeManager、DataNode、SecondaryNameNode</li>
</ul>
<h2>五、访问 Web 管理界面</h2>
<h3>1. 关闭防火墙（所有节点）</h3>
<pre><code>systemctl stop firewalld
systemctl disable firewalld
</code></pre>
<h3>2. 配置本地 hosts（Windows）</h3>
<p>编辑本地 Windows 的 <code>C:\Windows\System32\drivers\etc\hosts</code> 文件，添加：</p>
<pre><code>192.168.121.160 hadoop1
192.168.121.161 hadoop2
192.168.121.162 hadoop3
</code></pre>
<h3>3. 访问 Web UI</h3>
<ul>
<li><strong>HDFS 管理界面</strong>：http://hadoop1:9870</li>
<li><strong>YARN 资源管理器</strong>：http://hadoop2:8088</li>
</ul>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>Windows驱动程序是什么？</title>
    <link href="https://blog.yuk1.uk/posts/windows-driver/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/windows-driver/</id>
    <published>2025-11-30T00:00:00.000Z</published>
    <updated>2025-11-30T00:00:00.000Z</updated>
    <summary>Windows驱动程序的作用、安装方法及更新策略解析</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>刚装完 Windows，屏幕分辨率不对、没声音、连不上 Wi-Fi——这些问题的根源往往是<strong>驱动程序缺失</strong>。驱动程序是操作系统与硬件之间的翻译官，没有它，再强大的硬件也只是一堆沉默的硅片。</p>
<h1>什么是驱动程序</h1>
<h2>基本概念</h2>
<p>驱动程序（Device Driver）是一种使计算机与硬件设备进行通信的特殊程序。操作系统通过驱动程序提供的接口来控制硬件设备的工作。如果某设备的驱动程序未能正确安装，该设备便无法正常工作。</p>
<h2>驱动程序在系统中的位置</h2>
<table>
<thead>
<tr>
<th>层级</th>
<th>组件</th>
<th>权限级别</th>
</tr>
</thead>
<tbody>
<tr>
<td>用户层</td>
<td>应用程序</td>
<td>低</td>
</tr>
<tr>
<td>系统层</td>
<td>操作系统（Windows 内核）</td>
<td>高</td>
</tr>
<tr>
<td><strong>驱动层</strong></td>
<td><strong>设备驱动程序</strong></td>
<td><strong>高（内核态）</strong></td>
</tr>
<tr>
<td>硬件层</td>
<td>物理设备</td>
<td>—</td>
</tr>
</tbody>
</table>
<blockquote>
<p>[!WARNING]
驱动程序运行在内核态，拥有与操作系统相同的权限。安装来源不明的驱动相当于给系统开了一道后门。务必从官方或可信渠道获取驱动。</p>
</blockquote>
<h1>驱动程序分类</h1>
<h2>按功能分类</h2>
<table>
<thead>
<tr>
<th>类型</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>显示驱动</td>
<td>控制显卡输出画面</td>
<td>NVIDIA / AMD / Intel 显卡驱动</td>
</tr>
<tr>
<td>网络驱动</td>
<td>管理有线 / 无线网络通信</td>
<td>Wi-Fi 适配器、以太网卡驱动</td>
</tr>
<tr>
<td>声音驱动</td>
<td>处理音频输入输出</td>
<td>Realtek HD Audio、NVIDIA HDMI Audio</td>
</tr>
<tr>
<td>芯片组驱动</td>
<td>管理主板各组件间的通信</td>
<td>Intel Chipset、AMD Chipset</td>
</tr>
<tr>
<td>存储驱动</td>
<td>控制硬盘、SSD 等存储设备</td>
<td>NVMe Controller、RAID 驱动</td>
</tr>
<tr>
<td>USB 驱动</td>
<td>管理 USB 设备的识别与通信</td>
<td>USB 3.x 主控驱动</td>
</tr>
<tr>
<td>蓝牙驱动</td>
<td>管理蓝牙设备的配对与通信</td>
<td>Intel Bluetooth、Realtek Bluetooth</td>
</tr>
<tr>
<td>打印驱动</td>
<td>将打印任务发送至打印机</td>
<td>各品牌打印机驱动</td>
</tr>
</tbody>
</table>
<h2>驱动签名机制</h2>
<p>Windows 通过 <strong>驱动签名强制（Driver Signature Enforcement）</strong> 来保证驱动安全性：</p>
<pre><code>flowchart TD
    A["驱动程序"] --&gt; B{"是否持有&lt;br/&gt;微软数字签名？"}
    B -- 是 --&gt; C["允许安装并加载"]
    B -- 否 --&gt; D["默认阻止安装"]
    D --&gt; E{"用户手动确认？"}
    E -- 是 --&gt; F["仅在测试模式下允许"]
    E -- 否 --&gt; G["安装失败"]
</code></pre>
<blockquote>
<p>[!NOTE]
64 位 Windows 默认强制要求驱动签名。这意味着所有通过正常渠道安装的驱动都必须经过微软的数字签名认证。这一机制有效防止了恶意驱动的加载。</p>
</blockquote>
<h1>驱动安装</h1>
<h2>系统自动安装</h2>
<p>Windows 10/11 安装镜像（约 4 ~ 5 GB）内置了<strong>通用驱动程序库</strong>。系统首次启动时，会在后台自动为已检测到的硬件设备安装适配的驱动程序，确保基本功能正常运行。<br />
这种方式安装的驱动通常版本较旧，但胜在<strong>稳定兼容</strong>，足以让系统完成基本初始化。</p>
<h2>手动下载安装</h2>
<p>对于显卡、芯片组等核心驱动，建议从<strong>官方网站</strong>下载最新版本。</p>
<h3>显卡驱动</h3>
<table>
<thead>
<tr>
<th>厂商</th>
<th>官方下载地址</th>
<th>自动检测工具</th>
</tr>
</thead>
<tbody>
<tr>
<td>NVIDIA</td>
<td><a href="https://www.nvidia.cn/geforce/drivers/">GeForce 驱动</a></td>
<td>GeForce Experience</td>
</tr>
<tr>
<td>AMD</td>
<td><a href="https://www.amd.com/zh-hans/support">AMD 驱动支持</a></td>
<td>AMD Adrenalin</td>
</tr>
<tr>
<td>Intel</td>
<td><a href="https://www.intel.cn/content/www/cn/zh/download-center/home.html">Intel 驱动与支持</a></td>
<td>Intel Driver &amp; Support Assistant</td>
</tr>
</tbody>
</table>
<h3>其他硬件驱动</h3>
<table>
<thead>
<tr>
<th>设备类型</th>
<th>推荐下载渠道</th>
</tr>
</thead>
<tbody>
<tr>
<td>主板</td>
<td>品牌官网支持页面（华硕、微星、技嘉等）</td>
</tr>
<tr>
<td>笔记本</td>
<td>笔记本品牌官网（联想、戴尔、惠普等）</td>
</tr>
<tr>
<td>打印机</td>
<td>打印机品牌官网支持页面</td>
</tr>
<tr>
<td>网卡 / 声卡</td>
<td>主板官网或芯片厂商官网</td>
</tr>
</tbody>
</table>
<blockquote>
<p>[!TIP]
搜索驱动时建议安装广告拦截工具（如 <a href="https://adguard.com/zh_cn/welcome.html">AdGuard</a>），避免误入第三方驱动下载站——这些站点经常捆绑恶意软件或提供过期甚至伪造的驱动。</p>
</blockquote>
<h1>注意事项</h1>
<h2>下载路径 &amp; 安装位置选择</h2>
<p>将驱动安装包放在<strong>纯英文路径</strong>下，避免因路径包含中文字符导致安装失败。</p>
<pre><code>推荐：C:\Drivers\NVIDIA\
不推荐：C:\下载\显卡驱动\
</code></pre>
<p>对于显卡、芯片组等核心驱动，<strong>强烈建议使用默认安装路径</strong>。不要为了节省 C 盘空间而随意更改安装位置。</p>
<h2>驱动更新策略</h2>
<table>
<thead>
<tr>
<th>场景</th>
<th>是否需要更新</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>系统运行稳定，硬件全部正常</td>
<td>不需要</td>
<td>"能跑就别动"是驱动的黄金法则</td>
</tr>
<tr>
<td>出现蓝屏、卡顿等硬件异常</td>
<td>需要</td>
<td>可能是驱动 Bug，更新可能修复</td>
</tr>
<tr>
<td>新游戏 / 软件提示需更新驱动</td>
<td>需要</td>
<td>新软件通常依赖新版驱动的特性</td>
</tr>
<tr>
<td>安全漏洞公告</td>
<td>需要</td>
<td>尤其是网卡、存储等关键驱动</td>
</tr>
<tr>
<td>显卡驱动发布 Game Ready 版本</td>
<td>按需</td>
<td>专为某款新游戏优化，喜欢该游戏可更新</td>
</tr>
</tbody>
</table>
<blockquote>
<p>[!WARNING]
<strong>不要盲目追求最新驱动。</strong> 新版驱动的主要目标是适配新发布的硬件和修复已知问题。对于老硬件和稳定环境，新版驱动有时反而可能引入新的兼容性风险或 Bug。如果当前一切正常，保持现状往往是最稳妥的选择。</p>
</blockquote>
<h2>常见问题排查</h2>
<table>
<thead>
<tr>
<th>故障现象</th>
<th>可能原因</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>设备管理器显示黄色感叹号</td>
<td>驱动缺失或损坏</td>
<td>右键更新驱动 / 重新安装</td>
</tr>
<tr>
<td>设备管理器显示红色叉号</td>
<td>设备被禁用</td>
<td>右键 → 启用设备</td>
</tr>
<tr>
<td>更新驱动后蓝屏</td>
<td>新驱动与系统不兼容</td>
<td>进入安全模式，回滚驱动</td>
</tr>
<tr>
<td>显卡性能突然下降</td>
<td>驱动异常 / 被切换为集显</td>
<td>检查显示适配器列表，重装驱动</td>
</tr>
<tr>
<td>声音设备消失</td>
<td>声卡驱动丢失</td>
<td>设备管理器中扫描硬件变动</td>
</tr>
<tr>
<td>USB 设备无法识别</td>
<td>USB 驱动异常</td>
<td>卸载 USB 主控驱动后重启</td>
</tr>
</tbody>
</table>
<h1>总结</h1>
<p>驱动程序是连接操作系统与硬件的关键桥梁。管理驱动的核心原则可以概括为三点：</p>
<ol>
<li><strong>来源可靠</strong> — 优先使用系统自带驱动和官方渠道下载，不信任第三方驱动站</li>
<li><strong>稳定优先</strong> — 没有问题就不要更新，更新前阅读更新日志评估收益</li>
</ol>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Knowledge"/>
  </entry>
  <entry>
    <title>Mizuki实现Giscus评论区</title>
    <link href="https://blog.yuk1.uk/posts/mizuki-giscus/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/mizuki-giscus/</id>
    <published>2025-09-13T00:00:00.000Z</published>
    <updated>2026-03-31T00:00:00.000Z</updated>
    <summary>在Mizuki主题中实现Giscus评论区的教程</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<blockquote>
<p>[!TIP]
在 Mizuki 主题 9.0 版本默认已集成 Giscus 评论系统，本文适用于旧版本手动集成。</p>
</blockquote>
<p>Mizuki 主题默认使用 Twikoo 作为评论系统。Giscus 基于 GitHub Discussions，无需数据库、无第三方服务依赖，且支持 Markdown 和代码高亮。本文介绍如何在旧版 Mizuki 主题中手动集成 Giscus。</p>
<h1>准备工作</h1>
<h2>1. 创建评论仓库</h2>
<ol>
<li>在 GitHub 新建一个 <strong>公开（<code>public</code>）</strong> 仓库，用于存储评论数据。</li>
<li>进入仓库 <strong>Settings → General</strong>，在 <strong>Features</strong> 区域勾选 <strong>Discussions</strong> 并保存。</li>
</ol>
<h2>2. 获取 Giscus 配置</h2>
<p>访问 <a href="https://giscus.app/zh-CN">giscus.app</a>，按向导完成配置。完成后会生成如下代码，记下其中的关键参数：</p>
<pre><code>&lt;script src="https://giscus.app/client.js"
  data-repo="your-github-username/your-repo-name"
  data-repo-id="your-repo-id"
  data-category="Announcements"
  data-category-id="your-category-id"
  data-mapping="pathname"
  data-strict="0"
  data-reactions-enabled="1"
  data-emit-metadata="0"
  data-input-position="top"
  data-theme="preferred_color_scheme"
  data-lang="zh-CN"
  data-loading="lazy"
  crossorigin="anonymous"
  async&gt;
&lt;/script&gt;
</code></pre>
<h1>修改主题配置</h1>
<h2>1. 更新评论配置</h2>
<p>编辑 <code>src/config.ts</code>，将 Twikoo 配置替换为 Giscus：</p>
<pre><code> export const commentConfig: CommentConfig = {
-	enable: false,
-	twikoo: {
-		envId: "https://twikoo.vercel.app",
-		lang: "en",
-	},
+	enable: true,
+	giscus: {
+		enable: true,
+		repo: "your-github-username/your-repo-name",
+		repoId: "your-repo-id",
+		category: "Announcements",
+		categoryId: "your-category-id",
+		mapping: "pathname",
+		strict: "0",
+		reactionsEnabled: "1",
+		emitMetadata: "0",
+		inputPosition: "top",
+		theme: "preferred_color_scheme",
+		lang: "zh-CN",
+	},
 };
</code></pre>
<h2>2. 添加 Giscus 组件</h2>
<p>在 <code>src/components/comment/</code> 目录下新建 <code>Giscus.astro</code>（可删除原 <code>Twikoo.astro</code>）：</p>
<pre><code>---
import { commentConfig } from "@/config";

interface Props {
  path: string;
}

const giscusConfig = commentConfig.giscus;
---

&lt;div class="giscus-container"&gt;&lt;/div&gt;

&lt;script is:inline define:vars={{ giscusConfig }}&gt;
  (() =&gt; {
    const getTheme = () =&gt; document.documentElement.classList.contains('dark') ? 'dark' : 'light';

    const loadGiscus = () =&gt; {
      const root = document.querySelector('.giscus-container');
      if (!root || root.querySelector('iframe.giscus-frame')) return;

      const s = document.createElement('script');
      s.src = 'https://giscus.app/client.js';
      s.async = true;

      Object.entries({
        'data-repo': giscusConfig.repo,
        'data-repo-id': giscusConfig.repoId,
        'data-category': giscusConfig.category,
        'data-category-id': giscusConfig.categoryId,
        'data-mapping': giscusConfig.mapping || 'pathname',
        'data-strict': giscusConfig.strict || '0',
        'data-reactions-enabled': giscusConfig.reactionsEnabled || '1',
        'data-emit-metadata': giscusConfig.emitMetadata || '0',
        'data-input-position': giscusConfig.inputPosition || 'top',
        'data-lang': giscusConfig.lang || 'zh-CN',
        'data-loading': 'lazy',
        'data-theme': getTheme()
      }).forEach(([key, value]) =&gt; s.setAttribute(key, value));

      root.appendChild(s);
    };

    const updateTheme = () =&gt; {
      const iframe = document.querySelector('iframe.giscus-frame');
      if (iframe?.contentWindow) {
        iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: getTheme() } } }, 'https://giscus.app');
      }
    };

    loadGiscus();

    if (!window._giscusObserver) {
      const observer = new MutationObserver((mutations) =&gt; {
        if (mutations.some(m =&gt; m.type === 'attributes' &amp;&amp; m.attributeName === 'class')) {
          updateTheme();
        }
      });
      observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
      window._giscusObserver = observer;
    }

    const handleSwup = () =&gt; loadGiscus();
    if (window.swup) {
      window.swup.hooks.on('content:replace', handleSwup);
    } else {
      document.addEventListener('swup:enable', () =&gt; {
        window.swup?.hooks.on('content:replace', handleSwup);
      }, { once: true });
    }
  })();
&lt;/script&gt;

&lt;style&gt;
  .giscus-container {
    width: 100%;
    margin-top: 1rem;
    transition: color 0.15s, background-color 0.15s, border-color 0.15s;
  }
  .giscus-container iframe.giscus-frame {
    width: 100% !important;
    border-radius: var(--radius-large);
    border: 1px solid var(--line-divider);
    background: transparent;
    transition: border-color 0.15s, background-color 0.15s;
  }
  .giscus-container:hover iframe.giscus-frame {
    border-color: var(--primary);
  }
  :root:not(.dark) .giscus-container {
    color-scheme: light;
  }
  :root.dark .giscus-container {
    color-scheme: dark;
  }
&lt;/style&gt;
</code></pre>
<h2>3. 更新评论组件入口</h2>
<p>编辑 <code>src/components/comment/index.astro</code>：</p>
<pre><code>-import Twikoo from "./Twikoo.astro";
+import Giscus from "./Giscus.astro";

-if (commentConfig?.enable &amp;&amp; commentConfig?.twikoo) {
-	commentService = "twikoo";
+if (commentConfig?.enable) {
+	if (commentConfig?.giscus) {
+		commentService = "giscus";
+	}

-{commentService === 'twikoo' &amp;&amp; &lt;Twikoo path={path} /&gt;}
+{commentService === 'giscus' &amp;&amp; &lt;Giscus path={path} /&gt;}
</code></pre>
<h2>4. 添加类型定义</h2>
<p>编辑 <code>src/types/config.ts</code>，补充 Giscus 类型：</p>
<pre><code>-twikoo?: TwikooConfig;
+giscus?: GiscusConfig;

-type TwikooConfig = {
-	envId: string;
-	region?: string;
-};
+type GiscusConfig = {
+	enable: boolean;
+	repo: string;
+	repoId: string;
+	category: string;
+	categoryId: string;
+	mapping?: string;
+	strict?: string;
+	reactionsEnabled?: string;
+	emitMetadata?: string;
+	inputPosition?: string;
+	theme?: string;
+	lang?: string;
+};
</code></pre>
<h1>结语</h1>
<p>完成以上步骤后，你的 Mizuki 博客就成功集成了 Giscus 评论系统。现在，读者可以通过 GitHub Discussions 与您互动，享受更丰富的评论体验。希望这篇教程对你有所帮助！如果有任何问题或建议，欢迎在评论区留言讨论。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>我的世界大电影</title>
    <link href="https://blog.yuk1.uk/posts/a-minecraft-movie/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/a-minecraft-movie/</id>
    <published>2025-04-29T00:00:00.000Z</published>
    <updated>2025-04-29T00:00:00.000Z</updated>
    <summary>我的世界大电影观后有感</summary>
    <content type="html"><![CDATA[<p>全篇充斥着套路化的叙事情节，我的老天啊这简直就是马桶镶金边。<br />
《我的世界大电影》相较于多年前的《我的世界：故事模式》这部电影就像一部面向低龄化观众的敷衍之作。<br />
如果说《我的世界：故事模式》是9.8分，那么《我的世界大电影》就是零！蛋！</p>
<blockquote>
<p>个人评价：4/10，令人空悲切的粪作</p>
</blockquote>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Daily"/>
  </entry>
  <entry>
    <title>Python实训 | 办公自动化</title>
    <link href="https://blog.yuk1.uk/posts/python-hands-on-training/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/python-hands-on-training/</id>
    <published>2025-01-02T00:00:00.000Z</published>
    <updated>2025-01-02T00:00:00.000Z</updated>
    <summary>Python办公自动化实训笔记</summary>
    <content type="html"><![CDATA[<h1>一、读取Excel批量制作Word准考证</h1>
<h2>功能说明</h2>
<ol>
<li><strong>Excel文件读取</strong><br />
支持读取包含考生信息的Excel文件。能够准确提取Excel文件中的考生信息列，如姓名、准考证号等。</li>
<li><strong>Word准考证生成</strong><br />
创建一个Word准考证模板，包含必要的字段和格式设置。使用python-docx库将考生信息填充到模板中，生成完整的准考证文档。</li>
<li><strong>批量处理</strong><br />
遍历Excel文件中的考生数据，为每个考生生成一个独立的准考证文档。支持将生成的准考证文档保存到指定目录。</li>
<li><strong>错误处理</strong><br />
在读取Excel文件时，能够捕获并处理可能的异常，如文件不存在、格式错误等。在生成Word文档时，能够处理数据缺失或格式不匹配的情况，确保生成的准考证文档内容完整、格式规范。</li>
<li><strong>用户交互</strong><br />
提供命令行界面，允许用户选择Excel文件、设置输出目录等。可选：提供图形用户界面（GUI），简化用户操作。</li>
</ol>
<h2>核心代码实现</h2>
<pre><code># 导入pandas库，并将其简称为pd，用于数据处理
import pandas as pd
# 导入os库，用于与操作系统进行交互，例如文件和目录操作
import os
# 从docx库中导入Document类，用于操作Word文档
from docx import Document
# 导入Tk类和文件选择对话框相关的函数，用于与用户进行文件和目录选择的交互
from tkinter import Tk
from tkinter.filedialog import askopenfilename, askdirectory
# 尝试读取指定路径的Excel文件，如果文件不存在会打印提示信息并返回None，如果读取过程中发生其他异常也会打印错误信息并返回None
def read_excel_data(file_path):
    try:
        data = pd.read_excel(file_path)
        return data
    except FileNotFoundError:
        print("文件不存在，请检查文件路径！")
        return None
    except Exception as e:
        print(f"读取 Excel 文件时发生错误: {e}")
        return None
# 接受一行数据data_row和一个Word文档模板template_doc
def generate_credentials(data_row, template_doc):
    # 定义一个包含学号、姓名、性别和考点的字典data_dict
    data_dict = {'sno': data_row['学号'], 'name': data_row['姓名'], 'gender': data_row['性别'], 'local': data_row['考点']}
    # 遍历模板文档中的表格、行和单元格，将特定的占位符（如 {{sno}} 等）替换为相应的数据值
    for table in template_doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for key, value in data_dict.items():
                    cell.text = cell.text.replace(f"{{{{{key}}}}}", str(value))
    # 最后返回修改后的模板文档
    return template_doc
def batch_process(excel_file_path, output_directory, template_doc_path):
    # 首先调用read_excel_data读取Excel文件的数据
    data = read_excel_data(excel_file_path)
    # 如果数据读取成功，打开指定路径的Word模板文件
    if data is not None:
        for index, row in data.iterrows():
            # 每次都创建一个新的文档副本
            new_doc = Document(template_doc_path)
            # 对新副本进行修改
            new_doc = generate_credentials(row, new_doc)
            # 为生成的文档创建文件名
            file_name = f"{row['姓名']}_准考证.docx"
            # 为生成的文档指定保存路径
            file_path = os.path.join(output_directory, file_name)
            # 保存修改后的新副本
            new_doc.save(file_path)
def main():
    # 创建Tk根窗口，并隐藏
    root = Tk()
    root.withdraw()
    # 使用文件选择对话框让用户选择Excel文件、输出目录和Word模板文件
    excel_file_path = askopenfilename(title="选择 Excel 文件", filetypes=[("Excel 文件", "*.xlsx")])
    output_directory = askdirectory(title="选择输出目录")
    template_doc_path = askopenfilename(title="选择 Word 模板文件", filetypes=[("Word 文件", "*.docx")])
    # 调用batch_process函数进行批处理
    batch_process(excel_file_path, output_directory, template_doc_path)
# 确保代码在作为主程序运行时执行main函数
if __name__ == "__main__":
    main()
</code></pre>
<h1>二、批量制作PPT证书</h1>
<h2>功能说明</h2>
<ol>
<li><strong>模板读取与解析</strong><br />
能够读取PPT模板文件，并解析其中的占位符（placeholder）和布局。支持多种PPT模板格式（如.pptx）。</li>
<li><strong>数据源读取</strong><br />
支持读取Excel文件（如.xlsx）或CSV文件作为数据源。能够准确提取数据源中的获奖人员信息列，并与模板中的字段进行匹配。</li>
<li><strong>信息填充与生成</strong><br />
使用python-pptx库将获奖人员信息填充到PPT模板中，生成完整的PPT证书。支持批量处理，为每个获奖人员生成一个独立的PPT证书。可以根据需求自定义证书的样式和布局，如字体、颜色、图片等。</li>
<li><strong>文档保存与管理</strong><br />
将生成的PPT证书保存到指定目录，并支持自定义文件名格式。可以提供打印预览功能，方便用户检查生成的证书效果。</li>
</ol>
<h2>核心代码实现</h2>
<pre><code># 调用pptx中的Presentation类用于读取模板
from pptx import Presentation
# 调用pandas库以读取Excel文件
import pandas as pd
# 调用模板文件'奖状.pptx'
prs = Presentation("奖状.pptx")
# 获取占位符
#slide = prs.slides[0]
#for placeholder in slide.placeholders:
#    idx_ =placeholder.placeholder_format.idx
#    print(idx_)
#    placeholder.text=f"{idx_}"
# 保存包含占位符的pptx文件
#prs.save("奖状_占位符.pptx")
# 获取pptx模板id
#layouts = prs.slide_layouts
#print(len(layouts))
#for index,slide_layout in enumerate(layouts):
#    print(index,slide_layout.name)
# 使用pd.read_excel读取xlsx文件
df = pd.read_excel('名单.xlsx')
# 遍历批量生成PPT文件
for i in range(df.shape[0]):
    # 选择模板id
    slide = prs.slides.add_slide(prs.slide_layouts[1])
    # 将姓名导入PPT模板中的占位符13处
    slide.placeholders[13].text =f'{df.iloc[i,:]["姓名"]}'
    # 将称号导入PPT模板中的占位符14处
    slide.placeholders[14].text =f'{df.iloc[i,:]["称号"]}'
    # 将颁发日期导入PPT模板中的占位符15处
    slide.placeholders[15].text =f'{df.iloc[i,:]["颁发日期"]}'
# 保存文件,命名为'奖状_all'
prs.save('奖状_all.pptx')
</code></pre>
<blockquote>
<p><strong>提示</strong>：</p>
<ul>
<li>模板中的占位符索引（如 <code>13</code>、<code>14</code>、<code>15</code>）因模板结构而异，请先运行代码中注释掉的调试片段，以准确获取各占位符的实际索引。</li>
</ul>
</blockquote>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>使用Cloudflare实现域名重定向</title>
    <link href="https://blog.yuk1.uk/posts/cloudflare-redirect-guide/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/cloudflare-redirect-guide/</id>
    <published>2024-12-25T00:00:00.000Z</published>
    <updated>2025-11-29T00:00:00.000Z</updated>
    <summary>如何使用Cloudflare实现域名重定向，使你的域名管理更高效</summary>
    <content type="html"><![CDATA[<h1>使用 Cloudflare 实现域名重定向</h1>
<p>当你更换了域名，或者希望将多个域名统一指向同一个站点时，域名重定向是最常用的手段。借助 Cloudflare 的<strong>重定向规则</strong>，无需额外的服务器，只需几步配置即可实现。</p>
<h2>整体流程</h2>
<ol>
<li>添加 DNS 记录并启用 Cloudflare 代理</li>
<li>创建重定向规则，匹配源域名并指向目标地址</li>
</ol>
<h2>第一步：配置 DNS 记录</h2>
<p>登录 Cloudflare 控制面板，进入目标域名的 <strong>DNS</strong> 管理页面，添加一条 A 记录：</p>
<ul>
<li><strong>名称</strong>：填写子域名(如<code>www</code>)</li>
<li><strong>IPv4地址</strong>：8.8.8.8</li>
<li><strong>代理状态</strong>：点亮小黄云，即设置为 <strong>已代理</strong>。</li>
</ul>
<blockquote>
<p><strong>代理状态必须开启</strong>：只有当流量经过 Cloudflare（小黄云亮起），重定向规则才能被触发。如果使用 DNS Only（灰色云朵），流量直接到达源服务器，Cloudflare 无法介入。</p>
</blockquote>
<p><img src="./post-img/816117.webp" alt="816117 w-100%" title="Cloudflare A 记录配置示例" /></p>
<h2>第二步：创建重定向规则</h2>
<ol>
<li>
<p>进入 <strong>规则</strong> &gt; <strong>概述</strong> 页面，点击 <strong>创建规则</strong>，选择 <strong>重定向规则</strong>。</p>
</li>
<li>
<p>配置匹配条件——<strong>如果传入请求匹配…</strong>：<br />
选择 <strong>自定义筛选表达式</strong>，设置如下：</p>
<ul>
<li><strong>字段</strong>：<code>主机名</code></li>
<li><strong>运算符</strong>：<code>等于</code></li>
<li><strong>值</strong>：填入你希望被重定向的原始完整域名（例如 <code>www.727252.xyz</code>）</li>
</ul>
</li>
<li>
<p>配置重定向动作——<strong>然后…</strong>：</p>
<ul>
<li><strong>类型</strong>：<code>动态</code></li>
<li><strong>表达式</strong>：</li>
</ul>
<pre><code>concat("https://新域名", http.request.uri.path)
</code></pre>
<ul>
<li><strong>示例</strong>：</li>
</ul>
<pre><code>concat("https://szh.rainettle.top", http.request.uri.path)
</code></pre>
<ul>
<li><strong>状态代码</strong>：<code>301</code></li>
</ul>
<blockquote>
<p><strong>说明</strong>：</p>
<ul>
<li><strong>301 Moved Permanently（永久重定向）</strong><br />
资源已被<strong>永久</strong>移至新 URL。客户端应更新书签或链接，后续请求直接使用新地址。多数浏览器会将原始请求方法（如 <code>POST</code>）自动转换为 <code>GET</code>。</li>
<li><strong>302 Found（临时重定向）</strong><br />
资源<strong>临时</strong>位于另一个 URL。客户端应继续使用原地址发起后续请求。尽管规范未强制，但实际中浏览器通常会将 <code>POST</code> 等非 <code>GET</code> 请求转为 <code>GET</code> 重定向。</li>
<li><strong>303 See Other（查看其他位置）</strong><br />
响应可在另一个 URI 获取，且<strong>必须使用 <code>GET</code> 方法</strong>访问该 URI。常用于表单提交后重定向到结果页，以避免刷新导致重复提交。</li>
<li><strong>307 Temporary Redirect（临时重定向，方法不变）</strong><br />
资源临时迁移，但客户端<strong>必须保持原始请求方法和请求体不变</strong>（如 <code>POST</code> 仍为 <code>POST</code>），重新向新地址发送请求。</li>
<li><strong>308 Permanent Redirect（永久重定向，方法不变）</strong><br />
资源已被<strong>永久</strong>移至新 URL，且<strong>必须保留原始请求方法和请求体</strong>。是 301 的严格版本，适用于需要保证请求语义完整的 API 迁移场景。</li>
</ul>
</blockquote>
</li>
</ol>
<p><img src="./post-img/000433.webp" alt="000433 w-100%" title="Cloudflare 重定向规则配置界面" />
4. 检查所有配置项无误后，点击底部的 <strong>部署</strong> 按钮。规则通常在几秒内生效。</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>Python笔记 | 第8章 面向对象/编程题</title>
    <link href="https://blog.yuk1.uk/posts/python-notes1/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/python-notes1/</id>
    <published>2024-12-12T00:00:00.000Z</published>
    <updated>2024-12-12T00:00:00.000Z</updated>
    <summary>Python面向对象编程题笔记</summary>
    <content type="html"><![CDATA[<p>1.设计一个Circle（圆）类，该类中包括属性radius（半径），还包括__init__()、get_perimeter()（求周长）和get_area()（求面积）共三个方法。
设计完成后，创建Circle类的对象求圆的周长和面积。</p>
<pre><code>#有参构造
class Circle:
    def __init__(self, radius):
        self.radius = radius
    def get_perimeter(self):
        return 2 * 3.14 * self.radius
    def get_area(self):
        return 3.14 * self.radius * self.radius
circle = Circle(5)
print(f'圆的周长：{circle.get_perimeter()}')
print(f'圆的面积：{circle.get_area()}')
#无参构造
class Circle:
    def __init__(self):
        self.radius = 5
    def get_perimeter(self):
        return 2 * 3.14 * self.radius
    def get_area(self):
        return 3.14 * self.radius * self.radius
circle = Circle()
print(f'圆的周长：{circle.get_perimeter()}')
print(f'圆的面积：{circle.get_area()}')
</code></pre>
<p>2.设计一个Course（课程）类，该类中包括number（编号）、name（名称）、teacher（任课教师）、location（上课地点）共4个属性，其中location是私有属性；
还包括__init__()、show_info()（显示课程信息）共两个方法。设计完成后，创建Course类的对象显示课程的信息。</p>
<pre><code>#有参构造
class Course:
    def __init__(self,number,name,teacher,location):
        self.number=number
        self.name=name
        self.teacher=teacher
        self.__location=location
    def show_info(self):
        print(f'编号：{self.number}')
        print(f'名称：{self.name}')
        print(f'任课教师：{self.teacher}')
        print(f'上课地点：{self.__location}')        
course=Course('001','程序设计','江','信工楼407')
course.show_info()
#无参构造
class Course:
    def __init__(self):
        self.number='001'
        self.name='程序设计'
        self.teacher='江'
        self.__location='信工楼407'
    def show_info(self):
        print(f'编号：{self.number}')
        print(f'名称：{self.name}')
        print(f'任课教师：{self.teacher}')
        print(f'上课地点：{self.__location}')
course=Course()
course.show_info()
</code></pre>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Tutorial"/>
  </entry>
  <entry>
    <title>世界，您好！</title>
    <link href="https://blog.yuk1.uk/posts/hello-world/" rel="alternate" type="text/html"/>
    <id>https://blog.yuk1.uk/posts/hello-world/</id>
    <published>2024-12-11T00:00:00.000Z</published>
    <updated>2024-12-11T00:00:00.000Z</updated>
    <summary>时隔两年再度启航，共赴崭新的博客之旅</summary>
    <content type="html"><![CDATA[<p>博客最初尝试过使用 Emlog、Z-Blog 和 Typecho，后期选择了 WordPress。<br />
之后，我发现了基于 Astro 构建的 Mizuki 主题，便将博客迁移至 Mizuki。<br />
如今，博客时隔两年再度启航——愿我们共赴崭新的日月！</p>
]]></content>
    <author><name>鈴葉Yuki</name></author>
    <category term="Daily"/>
  </entry>
</feed>
