<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>编程代码归档 - 帝讯博客</title>
	<atom:link href="https://www.dixunblog.cn/category/jishushare/code/feed" rel="self" type="application/rss+xml" />
	<link>https://www.dixunblog.cn/category/jishushare/code</link>
	<description>致力于打造专业的互联网资讯平台</description>
	<lastBuildDate>Fri, 30 Oct 2026 03:15:02 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://cdn.hyclive.cn/dixunblog/2025/12/cropped-ico-32x32.png</url>
	<title>编程代码归档 - 帝讯博客</title>
	<link>https://www.dixunblog.cn/category/jishushare/code</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>💑 情侣小屋 · Couple Home——原创情侣小程序</title>
		<link>https://www.dixunblog.cn/2091.html</link>
					<comments>https://www.dixunblog.cn/2091.html#respond</comments>
		
		<dc:creator><![CDATA[老大]]></dc:creator>
		<pubDate>Fri, 30 Oct 2026 03:15:02 +0000</pubDate>
				<category><![CDATA[手机软件]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[情侣小屋]]></category>
		<category><![CDATA[情侣小程序]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/?p=2091</guid>

					<description><![CDATA[<p>一个给情侣用的微信小程序：把两个人的小日子都记在一起 —— 纪念日、日记、相册、实时定位、点菜做饭、日程、喜好、愿望清单、经期记录…… A WeChat Mini Program for couples to record their life together. ✨ 功能特性 首页按三类组织共 13 个页面： 分组 功能 说明 记录我们 纪念日 支持阳历 / 农历，自动倒计时与到期提醒 情侣日记 图文日记，支持心情、天气、定位标记，图片三列瀑布流 情侣相册 分类相册，双方共享，支持批量上传 一起生活 点菜做饭 内</p>
<p><a href="https://www.dixunblog.cn/2091.html">💑 情侣小屋 · Couple Home——原创情侣小程序</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>一个给情侣用的微信小程序：把两个人的小日子都记在一起 —— 纪念日、日记、相册、实时定位、点菜做饭、日程、喜好、愿望清单、经期记录……</p>
<p><em>A WeChat Mini Program for couples to record their life together.</em></p>
<h2>✨ 功能特性</h2>
<p>首页按三类组织共 <strong>13 个页面</strong>：</p>
<table>
<tbody>
<tr>
<td>分组</td>
<td>功能</td>
<td>说明</td>
</tr>
<tr>
<td><strong>记录我们</strong></td>
<td>纪念日</td>
<td>支持阳历 / 农历，自动倒计时与到期提醒</td>
</tr>
<tr>
<td></td>
<td>情侣日记</td>
<td>图文日记，支持心情、天气、定位标记，图片三列瀑布流</td>
</tr>
<tr>
<td></td>
<td>情侣相册</td>
<td>分类相册，双方共享，支持批量上传</td>
</tr>
<tr>
<td><strong>一起生活</strong></td>
<td>点菜做饭</td>
<td>内置 <strong>416 道</strong>家常菜谱（带用量与分步做法）+ 自定义菜谱 + 互相点菜</td>
</tr>
<tr>
<td></td>
<td>日程</td>
<td>约定日程，到点强提醒</td>
</tr>
<tr>
<td></td>
<td>愿望清单</td>
<td>双方各写各的心愿，共享给对方</td>
</tr>
<tr>
<td><strong>关心与日常</strong></td>
<td>TA 的喜好</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></td>
<td>我的</td>
<td>昵称、头像、登录状态管理</td>
</tr>
</tbody>
</table>
<ul>
<li><strong>游客可先逛</strong>：未登录也能看到首页品牌与天气，登录后再解锁情侣功能（符合审核合规）。</li>
</ul>
<ul>
<li><strong>实时天气</strong>基于 <a href="https://open-meteo.com">Open-Meteo</a>，<strong>完全免费、无需 API key</strong>。</li>
</ul>
<ul>
<li><strong>内置菜谱</strong>源自开源项目 <a href="https://github.com/Anduin2017/HowToCook">HowToCook</a>（The Unlicense，公共领域），共 416 道，离线可用。</li>
</ul>
<hr />
<h2>🛠 技术架构</h2>
<p>┌─────────────────────────────────────────────┐<br />
│ 微信小程序（前端） │<br />
│ 原生 WXML / WXSS / JS，无第三方 UI 框架 │<br />
│ pages/* 13 个页面 · utils/* 工具与数据访问层 │<br />
└───────────────┬─────────────────────────────┘<br />
│ @tencent-ai/workbuddy-cloud-sdk<br />
▼<br />
┌─────────────────────────────────────────────┐<br />
│ WorkBuddy Cloud（后端） │<br />
│ · 身份认证：微信登录 (wx.login + signIn…) │<br />
│ · 数据库：PostgreSQL + PostgREST │<br />
│ · 行级安全：RLS（couple_id 隔离，二次校验） │<br />
│ · 文件存储：相册图片 / 视频 │<br />
└─────────────────────────────────────────────┘</p>
<ul>
<li><strong>前端</strong>：原生微信小程序，UI 采用粉色系主题（<code>#ff6b81</code> 导航条、<code>#fff5f7</code> 背景）。</li>
</ul>
<ul>
<li><strong>后端</strong>：<a href="https://www.workbuddy.cn">WorkBuddy Cloud SDK</a> —— 提供基于 PostgreSQL 的数据库、身份认证与文件存储，接口风格类似 Supabase（<code>cloud.database.from('表').select()</code>）。</li>
</ul>
<ul>
<li><strong>数据隔离</strong>：每一张情侣表都强制带 <code>couple_id</code> 查询条件，并由数据库 <strong>RLS（行级安全）二次校验</strong>，<code>couple_id</code> 必须属于当前用户所在的 <code>couples</code> 行，保证情侣之间互不可见。</li>
</ul>
<ul>
<li><strong>天气</strong>：<code>utils/weather.js</code> 直接请求 Open-Meteo 公开接口，无需后端中转、无需密钥。</li>
</ul>
<ul>
<li><strong>订阅消息</strong>：纪念日、点菜、愿望三类微信订阅消息，模板字段组装见 <code>utils/message.js</code>。</li>
</ul>
<hr />
<h2>📁 目录结构</h2>
<pre>情侣小屋/
├── app.js / app.json / app.wxss # 小程序入口与全局配置
├── project.config.json # 项目配置（含 appid）
├── pages/ # 13 个页面，每个含 .js/.wxml/.wxss/.json
│ ├── index/ # 首页（Hero + 天气 + 最近纪念日 + 功能网格）
│ ├── bind/ # 绑定 / 解绑情侣
│ ├── anniversary/ # 纪念日
│ ├── diary/ # 情侣日记
│ ├── album/ # 情侣相册
│ ├── location/ # 实时位置
│ ├── recipe/ # 点菜做饭（菜谱库）
│ ├── mine/ # 我的
│ ├── weather/ # 天气
│ ├── schedule/ # 日程
│ ├── preference/ # TA 的喜好
│ ├── cycle/ # 经期记录
│ └── wish/ # 愿望清单
├── utils/ # 工具与数据访问层（核心逻辑）
│ ├── cloud.js # 云服务初始化（endpoint + publishableKey）
│ ├── api.js # 数据访问层：所有表的 CRUD 与 RLS 约定
│ ├── features.js # 首页功能入口清单（分组 / 文案）
│ ├── recipes.js # 内置 416 道菜谱（生成产物，勿手改）
│ ├── weather.js # Open-Meteo 天气封装
│ ├── message.js # 订阅消息内容组装
│ └── … # 倒计时、农历、定位、上传、日志等
├── scripts/
│ └── build-recipes-from-howtocook.py # 从 HowToCook 重新生成菜谱库
├── docs/ # 更细的配置文档（域名 / 订阅消息 / 菜谱）
│ ├── 菜谱库说明.md
│ ├── 订阅消息配置指南.md
│ ├── 订阅消息服务端对接.md
│ ├── 平台后台域名配置.md
│ ├── 天气域名配置.md
│ └── 定位地名配置说明.md
└── tests/ # Node 单元测试（18 套件）
├── project.test.js # 全量静态检查（0 错误 0 警告）
└── recipes.test.js 等</pre>
<hr />
<h2>🚀 部署与配置（面向开发者）</h2>
<h3>1. 前置条件</h3>
<ul>
<li><strong>微信开发者工具</strong>（最新版）</li>
</ul>
<ul>
<li>一个<strong>微信小程序 AppID</strong>（在 <a href="https://mp.weixin.qq.com">微信公众平台</a> 注册）</li>
</ul>
<ul>
<li>一个 <strong>WorkBuddy Cloud</strong> 云服务实例（在 WorkBuddy 平台为当前小程序开通云服务，得到 <code>endpoint</code> 与 <code>publishableKey</code>）</li>
</ul>
<h3>2. 获取代码</h3>
<p>git clone &lt;你的仓库地址&gt;<br />
cd &lt;项目目录&gt;</p>
<p><span style="color: #ff6600;">点击下方下载源码程序</span></p>
<h3>3. 配置云服务（必须）</h3>
<p>编辑 <code>utils/cloud.js</code>，把示例值替换成你在 WorkBuddy 云平台开通云服务后得到的配置：</p>
<pre>const publicConfig = {
endpoint: 'https://你的云服务地址',
publishableKey: '你的 publishableKey'
}</pre>
<blockquote><p>⚠️ <strong>切勿把真实的生产 <code>publishableKey</code> 提交到公开仓库</strong>（见下方「开源前检查清单」）。</p></blockquote>
<h3>4. 配置小程序 AppID（必须）</h3>
<p>编辑 <code>project.config.json</code>，将 <code>appid</code> 改为你自己的小程序 AppID：</p>
<p>{ &#8220;appid&#8221;: &#8220;你自己的小程序AppID&#8221; }</p>
<h3>5. 配置服务器域名（必须）</h3>
<p>在 <strong>微信公众平台 → 开发 → 开发管理 → 开发设置 → 服务器域名</strong> 中，把以下域名加入 <strong>request 合法域名</strong>：</p>
<table>
<tbody>
<tr>
<td>域名</td>
<td>用途</td>
</tr>
<tr>
<td><code>https://mp-api.app.workbuddy.host</code></td>
<td>WorkBuddy Cloud 接口</td>
</tr>
<tr>
<td><code>https://api.open-meteo.com</code></td>
<td>实时天气</td>
</tr>
</tbody>
</table>
<blockquote><p>本地调试可在「微信开发者工具 → 详情 → 本地设置」勾选「不校验合法域名」。详细见 <code>docs/平台后台域名配置.md</code>、<code>docs/天气域名配置.md</code>。</p></blockquote>
<h3>6. 配置订阅消息（可选）</h3>
<p>若需要纪念日 / 点菜 / 愿望的微信订阅消息推送，需在公众平台「订阅消息 → 我的模板」中申请对应模板，并把模板 ID 填入 <code>utils/subscribe.config.js</code>。字段组装规则见 <code>utils/message.js</code> 与 <code>docs/订阅消息配置指南.md</code>。</p>
<h3>7. 定位权限（可选）</h3>
<p><code>app.json</code> 已声明 <code>scope.userLocation</code> 与 <code>requiredPrivateInfos</code>（getLocation / chooseLocation）。首次使用定位功能时小程序会向用户申请授权。</p>
<h3>8. 编译运行</h3>
<p>用微信开发者工具打开项目根目录，编译即可预览。上传发布在微信开发者工具或 WorkBuddy 平台完成。</p>
<hr />
<h2>🗄 数据库表结构参考（需在云平台自行创建）</h2>
<p>本仓库提供<strong>数据访问层</strong>（<code>utils/api.js</code>），其中约定了以下表与字段。它们需在你的 WorkBuddy Cloud 实例中创建，并配置对应的 <strong>RLS 行级安全策略</strong>。</p>
<table>
<tbody>
<tr>
<td>表名</td>
<td>关键字段</td>
<td>说明</td>
</tr>
<tr>
<td><code>profiles</code></td>
<td><code>owner_id</code>(用户) · <code>owner_name</code> · <code>avatar_url</code> · <code>couple_id</code> · <code>couple_role</code></td>
<td>用户档案；更新策略只允许改自己的行</td>
</tr>
<tr>
<td><code>couples</code></td>
<td><code>user_a</code> · <code>user_b</code> · <code>invite_code</code> · <code>relation_date</code> · <code>relation_at</code></td>
<td>情侣关系；<code>user_a/user_b</code> 可为空表示待绑定</td>
</tr>
<tr>
<td><code>anniversaries</code></td>
<td><code>couple_id</code> · <code>title</code> · <code>calendar_type</code>(阳/农历) · <code>month</code> · <code>day</code> · <code>start_year</code> · <code>emoji</code> · <code>repeat_yearly</code> · <code>remind_days</code></td>
<td>纪念日</td>
</tr>
<tr>
<td><code>diaries</code></td>
<td><code>couple_id</code> · <code>owner_name</code> · <code>content</code> · <code>images</code>(数组) · <code>mood</code> · <code>weather</code> · <code>location_name</code> · <code>latitude</code> · <code>longitude</code></td>
<td>情侣日记</td>
</tr>
<tr>
<td><code>albums</code></td>
<td><code>couple_id</code> · <code>name</code> · <code>emoji</code> · <code>sort_order</code></td>
<td>相册分类</td>
</tr>
<tr>
<td><code>album_items</code></td>
<td><code>couple_id</code> · <code>album_id</code> · <code>media_type</code> · <code>file_path</code> · <code>thumb_path</code> · <code>width</code> · <code>height</code> · <code>remark</code></td>
<td>相册内容</td>
</tr>
<tr>
<td><code>locations</code></td>
<td><code>couple_id</code> · <code>owner_id</code> · <code>latitude</code> · <code>longitude</code> · <code>address</code> · <code>city</code> · <code>precision_m</code> · <code>address_manual</code></td>
<td>实时位置</td>
</tr>
<tr>
<td><code>recipe_orders</code></td>
<td><code>couple_id</code> · <code>recipe_id</code> · <code>dish_name</code> · <code>note</code> · <code>servings</code> · <code>status</code>(pending/done) · <code>done_at</code> · <code>cooker_id</code></td>
<td>互相点菜</td>
</tr>
<tr>
<td><code>recipes_custom</code></td>
<td><code>couple_id</code> · <code>name</code> · <code>emoji</code> · <code>category</code> · <code>taste</code> · <code>duration</code> · <code>servings</code> · <code>ingredients</code>(JSON) · <code>steps</code>(JSON) · <code>tips</code></td>
<td>双方自定义菜谱</td>
</tr>
<tr>
<td><code>reminders</code></td>
<td><code>couple_id</code> · <code>kind</code> · <code>title</code> · <code>body</code> · <code>link</code> · <code>remind_at</code> · <code>is_read</code></td>
<td>应用内提醒</td>
</tr>
<tr>
<td><code>subscriptions</code></td>
<td><code>owner_id</code> · <code>couple_id</code> · <code>template_id</code> · <code>subscribed</code></td>
<td>订阅消息开关</td>
</tr>
<tr>
<td><code>schedules</code></td>
<td><code>couple_id</code> · <code>title</code> · <code>category</code> · <code>event_date</code> · <code>event_time</code> · <code>repeat_rule</code> · <code>priority</code> · <code>remind_before</code> · <code>is_done</code></td>
<td>日程</td>
</tr>
<tr>
<td><code>preferences</code></td>
<td><code>couple_id</code> · <code>owner_name</code> · <code>category</code> · <code>title</code> · <code>detail</code> · <code>is_favorite</code></td>
<td>对方喜好</td>
</tr>
<tr>
<td><code>wishes</code></td>
<td><code>couple_id</code> · <code>owner_id</code> · <code>title</code> · <code>category</code> · <code>detail</code> · <code>target_date</code> · <code>status</code>(open/done) · <code>notified</code></td>
<td>愿望清单</td>
</tr>
<tr>
<td><code>cycles</code></td>
<td><code>couple_id</code> · <code>owner_name</code> · <code>start_date</code> · <code>end_date</code> · <code>cycle_length</code> · <code>period_length</code> · <code>flow</code> · <code>symptom</code> · <code>note</code></td>
<td>经期记录</td>
</tr>
</tbody>
</table>
<p><strong>安全相关的服务端对象</strong>（被 <code>api.js</code> 调用，但 SQL 不在本仓库，需在云平台以 <code>SECURITY DEFINER</code> 函数提供）：</p>
<ul>
<li><code>find_couple_by_invite(p_code)</code> —— 绑定前绕过 RLS 查找邀请码</li>
</ul>
<ul>
<li><code>accept_invite(p_code)</code> —— 绑定动作，绕过 RLS 写入 <code>couples</code></li>
</ul>
<ul>
<li><code>couples</code> 表的更新策略 <code>couples_update_member</code> —— 仅关系成员可更新</li>
</ul>
<blockquote><p>所有情侣表务必配置 RLS：<strong>每行查询强制 <code>couple_id</code> 条件 + 数据库层二次校验</strong>，且 <code>couple_id</code> 必须属于当前用户所在的 <code>couples</code> 行。这是数据隔离的底线，务必在云控制台核对。</p></blockquote>
<hr />
<h2>🔐 安全与隐私</h2>
<ul>
<li><strong>数据隔离</strong>：情侣数据通过 <code>couple_id</code> 在应用层与数据库层（RLS）双重隔离，情侣之间互不可见。</li>
</ul>
<ul>
<li><strong>凭据管理</strong>：<code>utils/cloud.js</code> 的 <code>publishableKey</code> 属于<strong>可公开</strong>的发布密钥（publishable key），但为安全起见，开源时仍建议替换为占位符或自建实例的密钥；<code>project.config.json</code> 的 <code>appid</code> 务必替换为你自己的。</li>
</ul>
<ul>
<li><strong>位置信息</strong>：仅在用户明确授权后采集，用于展示双方距离与日记定位，不上传无关信息。</li>
</ul>
<hr />
<h2>📖 使用说明</h2>
<p>面向最终用户的详细使用指南见 <strong>docs/使用说明.md</strong>，涵盖：如何登录与绑定情侣、各功能模块怎么用、常见问题。</p>
<hr />
<h2>🧪 测试</h2>
<pre>项目自带 Node 单元测试（纯逻辑层，不依赖微信运行时）：
node --test tests/*.test.js # 运行全部 18 套件
node tests/recipes.test.js # 仅校验菜谱库数据完整性
node tests/project.test.js # 全量静态检查（应 0 错误 0 警告）
测试覆盖：数据访问层字段映射、菜谱库完整性（字段齐全 / id 与菜名唯一 / 分类合法 / 分页逻辑）、首页功能清单一致性、各工具模块等。</pre>
<hr />
<h2>🍳 重新生成菜谱库（可选）</h2>
<pre>内置菜谱来自 <a href="https://github.com/Anduin2017/HowToCook">HowToCook</a>（The Unlicense）。<code>utils/recipes.js</code> 是<strong>生成产物，不要手改</strong>，改了会被重新生成覆盖。如需更新菜谱： git clone --depth 1 https://github.com/Anduin2017/HowToCook.git python scripts/build-recipes-from-howtocook.py &lt;HowToCook路径&gt; utils/recipes.js node tests/recipes.test.js # 生成后务必跑一次校验 生成规则与字段映射详见 <code>docs/菜谱库说明.md</code>。</pre>
<hr />
<h2>📦 开源前检查清单</h2>
<p>将本项目发布到公开仓库前，请确认：</p>
<ul data-type="todo">
<li><code>utils/cloud.js</code> 中的 <code>publishableKey</code> 与 <code>endpoint</code> 已替换为占位符或你自己的实例配置</li>
</ul>
<ul data-type="todo">
<li><code>project.config.json</code> 中的 <code>appid</code> 已替换为你自己的小程序 AppID</li>
</ul>
<ul data-type="todo">
<li>已在 WorkBuddy Cloud 实例中创建上述数据表并配置 RLS 策略（或已在文档中说明）</li>
</ul>
<ul data-type="todo">
<li>微信公众平台已配置服务器域名（<code>mp-api.app.workbuddy.host</code>、 <code>api.open-meteo.com</code>）</li>
</ul>
<ul data-type="todo">
<li>如需订阅消息，已配置模板 ID（见 <code>utils/subscribe.config.js</code>）</li>
</ul>
<ul data-type="todo">
<li>添加 <code>.gitignore</code> 忽略 <code>node_modules/</code>、<code>miniprogram_npm/</code> 等</li>
</ul>
<ul data-type="todo">
<li>在仓库根目录放置 <code>LICENSE</code> 文件</li>
</ul>
<hr />
<h2>📄 许可证与致谢</h2>
<ul>
<li><strong>本项目代码</strong>：采用 <strong>MIT License</strong>（见 <a href="LICENSE">LICENSE</a>）。</li>
</ul>
<ul>
<li><strong>内置菜谱数据</strong>：来自 <a href="https://github.com/Anduin2017/HowToCook">HowToCook</a>，许可为 <strong>The Unlicense</strong>（公共领域），可自由使用、修改、商用，无署名义务。菜谱页底部已标注来源。</li>
</ul>
<ul>
<li><strong>天气数据</strong>：来自 <a href="https://open-meteo.com">Open-Meteo</a>，免费公开气象数据。</li>
</ul>
<ul>
<li>感谢以上开源项目与数据服务，让这个小工具得以快速成型。</li>
</ul>
<p>&nbsp;</p>
<h1>情侣小屋 · 使用说明</h1>
<p>欢迎使用「情侣小屋」！这是一份写给<strong>使用者</strong>的指南，告诉你每个功能怎么玩。开发 / 部署相关内容请看仓库根目录的 <a href="../README.md">README.md</a>。</p>
<hr />
<h2>一、第一次打开</h2>
<ol>
<li>进入小程序后，首页会先展示品牌页和今天的天气。</li>
</ol>
<ol>
<li>点击 <strong>「一键登录」</strong>，用微信授权登录（不会读取你的微信昵称，昵称需要自己设置）。</li>
</ol>
<ol>
<li>登录后，首页会显示你和 TA 的专属卡片。</li>
</ol>
<blockquote><p>没绑定情侣前，大部分情侣功能处于「锁定」状态，先完成绑定即可解锁全部。</p></blockquote>
<hr />
<h2>二、绑定情侣（两个人都要做）</h2>
<p>情侣小屋是<strong>双人共享</strong>的，需要你们两个人都登录后「绑定」到一起。</p>
<p><strong>发起方（你）：</strong></p>
<ol>
<li>在首页点击「生成邀请码」（或在「绑定情侣」页操作）。</li>
</ol>
<ol>
<li>把生成的 6 位邀请码发给 TA，或直接在微信里<strong>分享小程序</strong>给 TA。</li>
</ol>
<p><strong>接受方（TA）：</strong></p>
<ol>
<li>打开小程序并登录。</li>
</ol>
<ol>
<li>进入「绑定情侣」页，输入你发来的邀请码，点击绑定。</li>
</ol>
<ol>
<li>绑定成功后，两人就进入同一个「小屋」了。</li>
</ol>
<p><strong>设置在一起的日子：</strong></p>
<ul>
<li>绑定后，在首页点击「在一起」面板，可以设置你们正式在一起的日期和时间。</li>
</ul>
<ul>
<li>之后首页会实时显示「我们已经在一起 N 天 HH:MM:SS」。</li>
</ul>
<blockquote><p>只有发起邀请的一方可以「解除绑定」。解除后，对方下次进入时会自动清理残留的绑定关系。</p></blockquote>
<hr />
<h2>三、记录我们</h2>
<h3>💗 纪念日</h3>
<ul>
<li>支持<strong>阳历</strong>和<strong>农历</strong>两种日期，可设每年的重复提醒。</li>
</ul>
<ul>
<li>可设置「提前 N 天提醒」，到期会通过应用内提醒 / 微信订阅消息通知你。</li>
</ul>
<ul>
<li>首页会自动显示<strong>最近的下一个纪念日</strong>和倒计时。</li>
</ul>
<h3>📖 情侣日记</h3>
<ul>
<li>写图文日记，记录每天的小事。</li>
</ul>
<ul>
<li>支持：文字、<strong>多张图片</strong>（每行 3 张）、心情、天气、定位标记。</li>
</ul>
<ul>
<li>双方都能看到、一起写，是你们的共同回忆。</li>
</ul>
<h3>📷 情侣相册</h3>
<ul>
<li>可以按主题建<strong>相册分类</strong>（比如「旅行」「美食」「宠物」）。</li>
</ul>
<ul>
<li>支持<strong>一次选多张</strong>批量上传，照片双方实时共享。</li>
</ul>
<ul>
<li>长按可删除。</li>
</ul>
<hr />
<h2>四、一起生活</h2>
<h3>🍳 点菜做饭</h3>
<ul>
<li>内置 <strong>416 道</strong>家常菜谱，带<strong>准确用量</strong>和<strong>分步做法</strong>，离线也能看。</li>
</ul>
<ul>
<li>顶部可按<strong>分类</strong>筛选、按<strong>菜名</strong>搜索，列表滚动到底自动加载更多。</li>
</ul>
<ul>
<li>点开一道菜可看到食材清单、做法步骤、小贴士（含热量）。</li>
</ul>
<ul>
<li><strong>互相点菜</strong>：看到想吃的菜，点「点它」下单，对方会收到通知；做好了可以标记为「已完成」。</li>
</ul>
<ul>
<li><strong>我喜欢吃的</strong>：在菜谱上标记 TA 爱吃的，下次点菜一眼就能看到。</li>
</ul>
<ul>
<li><strong>自定义菜谱</strong>：也可以自己添加拿手菜（带食材、做法）。</li>
</ul>
<h3>📅 日程</h3>
<ul>
<li>记录两人的约定（约会、电影、出差……）。</li>
</ul>
<ul>
<li>可设<strong>提醒提前量</strong>，临近时应用内强提醒。</li>
</ul>
<ul>
<li>支持单次 / 重复规则、优先级标记。</li>
</ul>
<h3>✨ 愿望清单</h3>
<ul>
<li>双方各写各的心愿（想去的地方、想要的东西……）。</li>
</ul>
<ul>
<li>写下的愿望会共享给对方，TA 看到后可以帮你实现；实现后标记为「已完成」。</li>
</ul>
<hr />
<h2>五、关心与日常</h2>
<h3>💝 TA 的喜好</h3>
<ul>
<li>记录对方的喜好，分类如「爱吃」「想去」等。</li>
</ul>
<ul>
<li>点菜时能直接看到 TA 爱吃的菜，贴心不踩雷。</li>
</ul>
<h3>📍 实时位置</h3>
<ul>
<li>授权定位后，可以看到<strong>双方的实时位置和距离</strong>。</li>
</ul>
<ul>
<li>适合「你到哪儿了」「我们差多远」这种场景。</li>
</ul>
<ul>
<li>也可以手动标注一个地点（比如「公司」「家」）。</li>
</ul>
<h3>🌸 经期记录</h3>
<ul>
<li>记录经期开始 / 结束，填写周期长度、经期长度等信息。</li>
</ul>
<ul>
<li>小程序会根据历史<strong>自动学习并预测</strong>下一次时间，方便提前关心。</li>
</ul>
<h3>🌤 天气</h3>
<ul>
<li>首页直接显示今天的天气、温度区间、风力湿度和<strong>穿衣建议</strong>。</li>
</ul>
<ul>
<li>数据来自 Open-Meteo，免费实时更新。</li>
</ul>
<hr />
<h2>六、我的</h2>
<ul>
<li>设置你的<strong>昵称</strong>和<strong>头像</strong>（微信登录不自动带昵称，建议第一时间设置，否则双方会显示为同一个名字）。</li>
</ul>
<ul>
<li>查看 / 管理登录状态。</li>
</ul>
<hr />
<h2>七、常见问题</h2>
<p><strong>Q：为什么我看不到对方写的日记 / 相册？</strong> A：请确认两人都已登录且<strong>成功绑定</strong>到同一个小屋（首页显示双方头像即表示已绑定）。</p>
<p><strong>Q：天气显示不出来？</strong> A：天气需要联网，且小程序后台需配置 <code>api.open-meteo.com</code> 为合法域名（开发者侧配置）。定位失败时会用默认城市兜底。</p>
<p><strong>Q：实时位置不准 / 不显示？</strong> A：首次使用需授权定位权限；在手机系统设置里给小程序开启「位置」权限即可。</p>
<p><strong>Q：邀请码用不了？</strong> A：不能用自己的邀请码绑定；邀请码已被使用或不存在都会提示。重新生成一个即可。</p>
<p><strong>Q：数据存在哪里？安全吗？</strong> A：数据按「情侣关系」隔离存储，只有你们两个人能看到彼此的内容。具体技术说明见 <a href="../README.md">README</a> 的「安全与隐私」章节。</p>
<p><strong>Q：菜谱能自己加吗？</strong> A：可以。在「点菜做饭」里添加自定义菜谱，只有你们两人共享。</p>
<hr />
<p>希望这个小屋，能装下你们的小日子 💗</p>
<h2></h2>
<h2>部分功能截图</h2>
<p><img fetchpriority="high" decoding="async" class="size-full wp-image-2092 aligncenter" title="03ad25f9fb7a3ac46812339ab5981aa4" src="http://cdn.hyclive.cn/dixunblog/2026/10/03ad25f9fb7a3ac46812339ab5981aa4.png" alt="03ad25f9fb7a3ac46812339ab5981aa4" width="556" height="1183" /></p>
<p><img decoding="async" class="size-full wp-image-2093 aligncenter" title="e15a051538830250bb32fcd770a2803f" src="http://cdn.hyclive.cn/dixunblog/2026/10/e15a051538830250bb32fcd770a2803f.png" alt="e15a051538830250bb32fcd770a2803f" width="547" height="1183" /><img decoding="async" class="size-full wp-image-2094 aligncenter" title="e8a1868a31d2b736b63be474de2d8dad" src="http://cdn.hyclive.cn/dixunblog/2026/10/e8a1868a31d2b736b63be474de2d8dad.png" alt="e8a1868a31d2b736b63be474de2d8dad" width="547" height="1189" /></p>
<p><a href="https://www.dixunblog.cn/2091.html">💑 情侣小屋 · Couple Home——原创情侣小程序</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/2091.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python 网络爬虫入门实战 &#8211; 实战篇</title>
		<link>https://www.dixunblog.cn/1455.html</link>
					<comments>https://www.dixunblog.cn/1455.html#respond</comments>
		
		<dc:creator><![CDATA[小编]]></dc:creator>
		<pubDate>Mon, 30 Mar 2026 02:05:58 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[Python 教程]]></category>
		<category><![CDATA[python-crawler]]></category>
		<category><![CDATA[代码教程]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/1455.html</guid>

					<description><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来学习 Python 网络爬虫入门实战。</p>
<p>一、环境准备</p>
<p><code class="language-bash">pip install requests beautifulsoup4 lxml</p>
<p>二、基础请求</p>
<p>&#60;code class=&#34;language-python...</p>
<p><a href="https://www.dixunblog.cn/1455.html">Python 网络爬虫入门实战 &#8211; 实战篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来学习 Python 网络爬虫入门实战。</p>
<h2><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1457" src="http://cdn.hyclive.cn/dixunblog/2026/03/生成-Python-爬虫入门实战图片.png" alt="" width="2730" height="1535" /></h2>
<h2>一、环境准备</h2>
<pre><code class="language-bash">pip install requests beautifulsoup4 lxml</code></pre>
<h2>二、基础请求</h2>
<pre><code class="language-python">import requests

# GET 请求
response = requests.get('https://www.example.com')
print(response.status_code)
print(response.text)</code></pre>
<h2>三、解析 HTML</h2>
<pre><code class="language-python">from bs4 import BeautifulSoup

html = '''</code></pre>
<h1>标题</h1>
<pre><code class="language-python"></code></pre>
<p>&#8221;&#8217; soup = BeautifulSoup(html, &#8216;lxml&#8217;) print(soup.h1.text)</p>
<pre><code class="language-python"></code></pre>
<h2>四、实战示例</h2>
<pre><code class="language-python"># 爬取新闻标题
import requests
from bs4 import BeautifulSoup

url = 'https://news.example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')

headlines = soup.find_all('h2', class_='headline')
for h in headlines:
    print(h.text)</code></pre>
<h2>五、注意事项</h2>
<ol>
<li>遵守 robots.txt 协议</li>
<li>控制请求频率</li>
<li>使用 User-Agent</li>
<li>合法合规使用数据</li>
</ol>
<h2>六、总结</h2>
<p>网络爬虫是 Python 实战的重要应用。建议多实践。</p>
<hr />
<p><em>关注我们获取更多 Python 实战教程！</em></p>
<p><a href="https://www.dixunblog.cn/1455.html">Python 网络爬虫入门实战 &#8211; 实战篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1455.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python 文件批量处理与自动化 &#8211; 进阶篇</title>
		<link>https://www.dixunblog.cn/1454.html</link>
					<comments>https://www.dixunblog.cn/1454.html#respond</comments>
		
		<dc:creator><![CDATA[小编]]></dc:creator>
		<pubDate>Mon, 30 Mar 2026 02:05:57 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[Python 教程]]></category>
		<category><![CDATA[python-file]]></category>
		<category><![CDATA[代码教程]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/1454.html</guid>

					<description><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来深入学习 Python 文件批量处理与自动化。这是每个 Python 开发者都必须掌握的核心技能。无论是处理日志文件、批量重命名、数据清洗。还是自动化办公。都离不开文件操作。</p>
<p>一、文件读写基础</p>
<p>1.1 打开文件的正确方式<br />
Python 使用 open() 函数打开文...</p>
<p><a href="https://www.dixunblog.cn/1454.html">Python 文件批量处理与自动化 &#8211; 进阶篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来深入学习 Python 文件批量处理与自动化。这是每个 Python 开发者都必须掌握的核心技能。无论是处理日志文件、批量重命名、数据清洗。还是自动化办公。都离不开文件操作。</p>
<h2><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1442" src="http://cdn.hyclive.cn/dixunblog/2026/03/生成Python办公插图.png" alt="" width="2730" height="1535" /></h2>
<h2>一、文件读写基础</h2>
<h3>1.1 打开文件的正确方式</h3>
<p>Python 使用 <code>open()</code> 函数打开文件。推荐使用 <code>with</code> 语句。它会自动关闭文件：</p>
<pre><code class="language-python"># 推荐方式 - with 语句自动关闭文件
with open('data.txt', 'r', encoding='utf-8') as f:
    content = f.read()
# 文件自动关闭。无需手动 f.close()

# 不推荐 - 需要手动关闭
f = open('data.txt', 'r', encoding='utf-8')
content = f.read()
f.close()  # 忘记关闭会导致资源泄漏</code></pre>
<h3>1.2 文件打开模式详解</h3>
<table class="wp-block-table">
<thead>
<tr>
<th>模式</th>
<th>说明</th>
<th>文件不存在</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>'r'</code></td>
<td>只读（默认）</td>
<td>报错</td>
</tr>
<tr>
<td><code>'w'</code></td>
<td>写入（清空原文件）</td>
<td>创建新文件</td>
</tr>
<tr>
<td><code>'a'</code></td>
<td>追加（在末尾添加）</td>
<td>创建新文件</td>
</tr>
<tr>
<td><code>'x'</code></td>
<td>独占创建</td>
<td>报错（如果已存在）</td>
</tr>
<tr>
<td><code>'b'</code></td>
<td>二进制模式</td>
<td>配合其他模式使用</td>
</tr>
<tr>
<td><code>'t'</code></td>
<td>文本模式（默认）</td>
<td>配合其他模式使用</td>
</tr>
</tbody>
</table>
<h3>1.3 读取文件的三种方法</h3>
<pre><code class="language-python"># 方法 1：read() - 读取整个文件
with open('data.txt', 'r', encoding='utf-8') as f:
    content = f.read()  # 返回字符串
    print(f"文件大小：{len(content)} 字节")

# 方法 2：readline() - 逐行读取
with open('data.txt', 'r', encoding='utf-8') as f:
    line1 = f.readline()  # 读取第一行
    line2 = f.readline()  # 读取第二行
    print(f"第一行：{line1.strip()}")

# 方法 3：readlines() - 读取所有行到列表
with open('data.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()  # 返回字符串列表
    print(f"共 {len(lines)} 行")
    for i, line in enumerate(lines, 1):
        print(f"第{i}行：{line.strip()}")

# 推荐：直接迭代文件对象（最省内存）
with open('data.txt', 'r', encoding='utf-8') as f:
    for line_num, line in enumerate(f, 1):
        print(f"第{line_num}行：{line.strip()}")</code></pre>
<h3>1.4 写入文件的多种方式</h3>
<pre><code class="language-python"># 写入模式（会清空原文件）
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write("第一行内容\n")
    f.write("第二行内容\n")
    f.write("第三行内容\n")

# 追加模式（在末尾添加）
with open('output.txt', 'a', encoding='utf-8') as f:
    f.write("追加的内容\n")

# 一次性写入多行
lines = ["苹果\n", "香蕉\n", "橙子\n"]
with open('fruits.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)  # 注意：需要自己添加换行符

# 使用 print 写入文件
with open('output.txt', 'w', encoding='utf-8') as f:
    print("第一行", file=f)
    print("第二行", file=f)
    print(f"变量值：{42}", file=f)</code></pre>
<h2>二、pathlib &#8211; 现代化的路径操作</h2>
<h3>2.1 Path 对象基础</h3>
<pre><code class="language-python">from pathlib import Path

# 创建路径对象
p = Path('/home/user/documents/report.txt')

# 路径组成部分
print(p.parent)        # /home/user/documents（父目录）
print(p.name)          # report.txt（文件名）
print(p.stem)          # report（不含扩展名）
print(p.suffix)        # .txt（扩展名）
print(p.suffixes)      # ['.txt']（所有扩展名）

# 路径拼接（推荐方式）
base = Path('/home/user')
subdir = base / 'documents' / '2026'
file_path = subdir / 'report.txt'
print(file_path)  # /home/user/documents/2026/report.txt</code></pre>
<h3>2.2 路径判断与转换</h3>
<pre><code class="language-python">from pathlib import Path

p = Path('/home/user/documents')

# 路径判断
print(p.exists())        # 是否存在
print(p.is_file())       # 是否是文件
print(p.is_dir())        # 是否是目录
print(p.is_absolute())   # 是否是绝对路径

# 路径转换
print(p.absolute())      # 转为绝对路径
print(p.resolve())       # 解析符号链接后的绝对路径
print(p.relative_to('/home/user'))  # documents

# 获取当前工作目录
cwd = Path.cwd()
print(f"当前目录：{cwd}")

# 获取家目录
home = Path.home()
print(f"家目录：{home}")</code></pre>
<h2>三、批量处理文件实战</h2>
<h3>3.1 遍历目录树</h3>
<pre><code class="language-python">import os
from pathlib import Path

# 方法 1：os.walk() - 遍历目录树
for root, dirs, files in os.walk('./documents'):
    print(f"当前目录：{root}")
    print(f"子目录：{dirs}")
    print(f"文件：{files}")
    print("-" * 40)

# 方法 2：Path.glob() - 模式匹配
doc_dir = Path('./documents')

# 查找所有.txt 文件
txt_files = list(doc_dir.glob('*.txt'))
print(f"找到 {len(txt_files)} 个 txt 文件")

# 递归查找所有子目录中的.txt 文件
all_txt = list(doc_dir.rglob('*.txt'))
print(f"递归找到 {len(all_txt)} 个 txt 文件")

# 查找特定模式的文件
py_files = list(doc_dir.glob('**/*.py'))  # ** 表示递归
print(f"找到 {len(py_files)} 个 Python 文件")</code></pre>
<h3>3.2 批量重命名文件</h3>
<pre><code class="language-python">from pathlib import Path

# 批量重命名：给所有文件添加前缀
doc_dir = Path('./documents')
for file in doc_dir.glob('*.txt'):
    new_name = f"backup_{file.name}"
    file.rename(file.parent / new_name)
    print(f"重命名：{file.name} -&gt; {new_name}")

# 批量修改扩展名
for file in doc_dir.glob('*.txt'):
    new_name = file.with_suffix('.md')
    file.rename(new_name)
    print(f"修改扩展名：{file.name} -&gt; {new_name.name}")

# 按序号重命名
for i, file in enumerate(doc_dir.glob('*.md'), 1):
    new_name = file.parent / f"document_{i:03d}.md"
    file.rename(new_name)
    print(f"重命名：{file.name} -&gt; {new_name.name}")</code></pre>
<h3>3.3 批量读取与处理</h3>
<pre><code class="language-python">from pathlib import Path

# 批量读取多个文件并合并
doc_dir = Path('./documents')
all_content = []

for file in sorted(doc_dir.glob('*.txt')):
    with open(file, 'r', encoding='utf-8') as f:
        content = f.read()
        all_content.append(f"=== {file.name} ===\n{content}")

# 合并写入新文件
with open('merged.txt', 'w', encoding='utf-8') as f:
    f.write('\n'.join(all_content))

print(f"已合并 {len(all_content)} 个文件到 merged.txt")

# 批量统计文件信息
print("\n文件统计：")
for file in sorted(doc_dir.glob('*.txt')):
    stat = file.stat()
    size_kb = stat.st_size / 1024
    print(f"{file.name}: {size_kb:.2f} KB")</code></pre>
<h3>3.4 文件内容搜索与替换</h3>
<pre><code class="language-python">from pathlib import Path
import re

# 批量搜索包含特定关键词的文件
doc_dir = Path('./documents')
keyword = "Python"
matched_files = []

for file in doc_dir.glob('*.txt'):
    with open(file, 'r', encoding='utf-8') as f:
        content = f.read()
        if keyword in content:
            matched_files.append(file.name)
            count = content.count(keyword)
            print(f"{file.name}: 找到 {count} 处匹配")

print(f"\n共 {len(matched_files)} 个文件包含'{keyword}'")

# 批量替换文本
old_text = "旧版本"
new_text = "新版本"

for file in doc_dir.glob('*.txt'):
    with open(file, 'r', encoding='utf-8') as f:
        content = f.read()
    
    if old_text in content:
        new_content = content.replace(old_text, new_text)
        with open(file, 'w', encoding='utf-8') as f:
            f.write(new_content)
        print(f"已更新：{file.name}")</code></pre>
<h2>四、高级文件操作</h2>
<h3>4.1 文件复制、移动与删除</h3>
<pre><code class="language-python">from pathlib import Path
import shutil

# 复制文件
src = Path('./source.txt')
dst = Path('./backup/source_copy.txt')
dst.parent.mkdir(parents=True, exist_ok=True)  # 创建目录
shutil.copy2(src, dst)  # copy2 保留元数据
print(f"已复制：{src} -&gt; {dst}")

# 复制整个目录
shutil.copytree('./docs', './docs_backup')
print("已复制整个目录")

# 移动文件
src = Path('./old_location/file.txt')
dst = Path('./new_location/file.txt')
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(src, dst)
print(f"已移动：{src} -&gt; {dst}")

# 删除文件
Path('./temp.txt').unlink()  # 删除单个文件
print("已删除 temp.txt")

# 删除整个目录
shutil.rmtree('./old_backup')
print("已删除目录")</code></pre>
<h3>4.2 临时文件处理</h3>
<pre><code class="language-python">import tempfile
from pathlib import Path

# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
    temp_path = f.name
    f.write("临时内容")
    print(f"临时文件：{temp_path}")

# 使用临时文件
with open(temp_path, 'r') as f:
    print(f"读取临时文件：{f.read()}")

# 清理临时文件
Path(temp_path).unlink()
print("已清理临时文件")

# 创建临时目录
with tempfile.TemporaryDirectory() as temp_dir:
    print(f"临时目录：{temp_dir}")
    # 在临时目录中操作
    temp_file = Path(temp_dir) / 'test.txt'
    temp_file.write_text("测试内容")
# 退出 with 后自动清理临时目录</code></pre>
<h3>4.3 文件编码处理</h3>
<pre><code class="language-python">import chardet

# 检测文件编码
def detect_encoding(file_path):
    with open(file_path, 'rb') as f:
        result = chardet.detect(f.read(10000))  # 读取前 10KB
    return result['encoding']

# 批量转换编码
from pathlib import Path

doc_dir = Path('./documents')
for file in doc_dir.glob('*.txt'):
    # 检测编码
    encoding = detect_encoding(file)
    print(f"{file.name}: 检测到编码 {encoding}")
    
    # 读取并转换为 UTF-8
    with open(file, 'r', encoding=encoding) as f:
        content = f.read()
    
    # 写回 UTF-8
    with open(file, 'w', encoding='utf-8') as f:
        f.write(content)
    print(f"已转换：{file.name}")</code></pre>
<h2>五、实战项目</h2>
<h3>5.1 日志文件分析器</h3>
<pre><code class="language-python">from pathlib import Path
from collections import Counter
import re

def analyze_log(log_path):
    '''分析日志文件'''
    log_file = Path(log_path)
    
    error_count = 0
    warning_count = 0
    error_lines = []
    
    with open(log_file, 'r', encoding='utf-8') as f:
        for line_num, line in enumerate(f, 1):
            if 'ERROR' in line:
                error_count += 1
                error_lines.append((line_num, line.strip()))
            elif 'WARNING' in line:
                warning_count += 1
    
    print(f"日志分析结果：")
    print(f"  错误数：{error_count}")
    print(f"  警告数：{warning_count}")
    print(f"\n最新错误：")
    for line_num, line in error_lines[-5:]:
        print(f"  行{line_num}: {line}")

# 使用示例
analyze_log('./app.log')</code></pre>
<h3>5.2 批量图片重命名</h3>
<pre><code class="language-python">from pathlib import Path
from datetime import datetime

def rename_photos(photo_dir, prefix="IMG"):
    '批量重命名照片'
    photo_path = Path(photo_dir)
    
    # 获取所有图片文件
    images = list(photo_path.glob('*.jpg')) + list(photo_path.glob('*.png'))
    images.sort(key=lambda p: p.stat().st_mtime)  # 按修改时间排序
    
    for i, img in enumerate(images, 1):
        # 生成新文件名
        date_str = datetime.fromtimestamp(img.stat().st_mtime).strftime('%Y%m%d')
        new_name = f"{prefix}_{date_str}_{i:04d}{img.suffix}"
        new_path = img.parent / new_name
        
        # 重命名
        img.rename(new_path)
        print(f"{img.name} -&gt; {new_name}")

# 使用示例
rename_photos('./photos', 'VACATION')</code></pre>
<h3>5.3 文件备份工具</h3>
<pre><code class="language-python">from pathlib import Path
import shutil
from datetime import datetime

def backup_files(source_dir, backup_dir, patterns=None):
    '备份指定类型的文件'
    source = Path(source_dir)
    backup = Path(backup_dir)
    
    # 创建带时间戳的备份目录
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    backup_path = backup / f"backup_{timestamp}"
    backup_path.mkdir(parents=True, exist_ok=True)
    
    # 默认备份所有文件
    if patterns is None:
        patterns = ['*']
    
    # 复制文件
    copied_count = 0
    for pattern in patterns:
        for file in source.glob(pattern):
            if file.is_file():
                dest = backup_path / file.name
                shutil.copy2(file, dest)
                copied_count += 1
    
    print(f"备份完成：{copied_count} 个文件")
    print(f"备份位置：{backup_path}")
    return backup_path

# 使用示例
backup_files('./documents', './backups', ['*.txt', '*.docx', '*.pdf'])</code></pre>
<h2>六、常见错误与解决</h2>
<h3>6.1 文件不存在错误</h3>
<pre><code class="language-python">from pathlib import Path

# 错误：文件不存在会报错
# with open('not_exist.txt', 'r') as f:
#     content = f.read()  # FileNotFoundError!

# 解决：先检查是否存在
file = Path('not_exist.txt')
if file.exists():
    content = file.read_text()
else:
    print("文件不存在。创建新文件")
    file.write_text("初始内容")

# 或使用异常处理
try:
    content = file.read_text()
except FileNotFoundError:
    print("文件不存在。使用默认内容")
    content = "默认内容"</code></pre>
<h3>6.2 编码错误</h3>
<pre><code class="language-python"># 错误：编码不匹配
# with open('file.txt', 'r', encoding='ascii') as f:
#     content = f.read()  # UnicodeDecodeError!

# 解决：使用正确的编码
with open('file.txt', 'r', encoding='utf-8') as f:
    content = f.read()

# 或忽略错误
with open('file.txt', 'r', encoding='utf-8', errors='ignore') as f:
    content = f.read()

# 或替换错误字符
with open('file.txt', 'r', encoding='utf-8', errors='replace') as f:
    content = f.read()</code></pre>
<h3>6.3 权限错误</h3>
<pre><code class="language-python">from pathlib import Path

# 错误：没有写入权限
# Path('/root/protected.txt').write_text("内容")  # PermissionError!

# 解决：检查权限或选择其他目录
file = Path('./user_file.txt')
try:
    file.write_text("内容")
except PermissionError:
    print("没有权限。尝试其他目录")
    file = Path.home() / 'file.txt'
    file.write_text("内容")</code></pre>
<h2>七、最佳实践</h2>
<ol>
<li><strong>始终使用 with 语句</strong>：自动关闭文件。避免资源泄漏</li>
<li><strong>明确指定编码</strong>：始终使用 <code>encoding='utf-8'</code></li>
<li><strong>使用 pathlib</strong>：比 os.path 更现代、更易用</li>
<li><strong>大文件分块读取</strong>：避免一次性加载到内存
<pre><code class="language-python">with open('large_file.txt', 'r') as f:
    for chunk in iter(lambda: f.read(8192), ''):
        process(chunk)</code></pre>
</li>
<li><strong>先创建目录再写入</strong>：
<pre><code class="language-python">output = Path('./output/subdir/file.txt')
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text("内容")</code></pre>
</li>
</ol>
<h2>八、总结与练习</h2>
<p>今天我们深入学习了 Python 文件处理的各个方面。建议：</p>
<ol>
<li><strong>掌握基础</strong>：open()、read()、write()、with 语句</li>
<li><strong>熟练使用 pathlib</strong>：现代化的路径操作</li>
<li><strong>理解编码</strong>：UTF-8 是默认选择</li>
<li><strong>多实践</strong>：通过实际项目巩固知识</li>
</ol>
<h3>课后练习</h3>
<ol>
<li>编写脚本。统计目录下所有代码文件的总行数</li>
<li>实现一个简单的文件搜索工具。支持关键词搜索</li>
<li>编写批量重命名工具。支持添加前缀、后缀、序号</li>
<li>实现日志分析器。提取错误和警告信息</li>
<li>创建自动备份脚本。定期备份重要文件</li>
</ol>
<hr />
<p><em>关注我们获取更多 Python 实战教程！下节课我们将学习 Python 网络爬虫入门。</em></p>
<p><a href="https://www.dixunblog.cn/1454.html">Python 文件批量处理与自动化 &#8211; 进阶篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1454.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python 自动化办公实战：Excel 处理 &#8211; 基础篇</title>
		<link>https://www.dixunblog.cn/1453.html</link>
					<comments>https://www.dixunblog.cn/1453.html#respond</comments>
		
		<dc:creator><![CDATA[小编]]></dc:creator>
		<pubDate>Mon, 30 Mar 2026 02:05:55 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[Python 教程]]></category>
		<category><![CDATA[python-excel]]></category>
		<category><![CDATA[代码教程]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/1453.html</guid>

					<description><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来学习 Python 自动化办公之 Excel 处理。</p>
<p>一、环境准备</p>
<p>1.1 安装库<br />
<code class="language-bash">pip install openpyxl pandas xlrd xlwt</p>
<p>1.2 库的选择</p>
<p><strong>openpy...</p>
<p><a href="https://www.dixunblog.cn/1453.html">Python 自动化办公实战：Excel 处理 &#8211; 基础篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>欢迎来到今天的 Python 实战教程！今天我们来学习 Python 自动化办公之 Excel 处理。</p>
<h2><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1442" src="http://cdn.hyclive.cn/dixunblog/2026/03/生成Python办公插图.png" alt="" width="2730" height="1535" /></h2>
<h2>一、环境准备</h2>
<h3>1.1 安装库</h3>
<pre><code class="language-bash">pip install openpyxl pandas xlrd xlwt</code></pre>
<h3>1.2 库的选择</h3>
<ul>
<li><strong>openpyxl</strong>：读写.xlsx 文件</li>
<li><strong>pandas</strong>：数据处理和分析</li>
<li><strong>xlrd/xlwt</strong>：读写.xls 文件（旧格式）</li>
</ul>
<h2>二、读取 Excel 文件</h2>
<h3>2.1 使用 openpyxl</h3>
<pre><code class="language-python">from openpyxl import load_workbook

# 加载工作簿
wb = load_workbook('data.xlsx')

# 选择工作表
ws = wb['Sheet1']

# 读取单元格
value = ws['A1'].value

# 遍历行
for row in ws.iter_rows():
    for cell in row:
        print(cell.value)</code></pre>
<h3>2.2 使用 pandas</h3>
<pre><code class="language-python">import pandas as pd

# 读取 Excel
df = pd.read_excel('data.xlsx')

# 查看数据
print(df.head())
print(df.columns)

# 选择列
names = df['姓名']</code></pre>
<h2>三、写入 Excel 文件</h2>
<h3>3.1 创建新文件</h3>
<pre><code class="language-python">from openpyxl import Workbook

wb = Workbook()
ws = wb.active
ws.title = "数据表"

# 写入数据
ws['A1'] = "姓名"
ws['B1'] = "年龄"
ws.append(["张三", 25])
ws.append(["李四", 28])

wb.save('output.xlsx')</code></pre>
<h3>3.2 使用 pandas 写入</h3>
<pre><code class="language-python">import pandas as pd

data = {
    '姓名': ['张三', '李四'],
    '年龄': [25, 28],
    '城市': ['北京', '上海']
}

df = pd.DataFrame(data)
df.to_excel('output.xlsx', index=False)</code></pre>
<h2>四、数据处理实战</h2>
<h3>4.1 数据筛选</h3>
<pre><code class="language-python"># 筛选年龄大于 25 的记录
filtered = df[df['年龄'] &gt; 25]

# 多条件筛选
filtered = df[(df['年龄'] &gt; 25) &amp; (df['城市'] == '北京')]</code></pre>
<h3>4.2 数据统计</h3>
<pre><code class="language-python"># 平均值
avg_age = df['年龄'].mean()

# 分组统计
grouped = df.groupby('城市')['年龄'].mean()</code></pre>
<h3>4.3 数据合并</h3>
<pre><code class="language-python"># 合并两个 Excel
df1 = pd.read_excel('file1.xlsx')
df2 = pd.read_excel('file2.xlsx')

# 横向合并
merged = pd.merge(df1, df2, on='姓名')

# 纵向合并
combined = pd.concat([df1, df2])</code></pre>
<h2>五、批量处理</h2>
<h3>5.1 批量读取</h3>
<pre><code class="language-python">import os
import pandas as pd

files = [f for f in os.listdir('.') if f.endswith('.xlsx')]
all_data = []

for file in files:
    df = pd.read_excel(file)
    all_data.append(df)

combined = pd.concat(all_data)</code></pre>
<h3>5.2 批量写入</h3>
<pre><code class="language-python">departments = ['销售部', '技术部', '财务部']

for dept in departments:
    df = get_department_data(dept)
    df.to_excel(f'{dept}_报表.xlsx', index=False)</code></pre>
<h2>六、格式化与样式</h2>
<pre><code class="language-python">from openpyxl.styles import Font, PatternFill

# 设置字体
ws['A1'].font = Font(bold=True, color='FF0000')

# 设置背景色
ws['A1'].fill = PatternFill(start_color='FFFF00', fill_type='solid')

# 设置列宽
ws.column_dimensions['A'].width = 20</code></pre>
<h2>七、总结</h2>
<p>Python 处理 Excel 可以大幅提升办公效率。建议多实践。</p>
<hr />
<p><em>关注我们获取更多 Python 实战教程！</em></p>
<p><a href="https://www.dixunblog.cn/1453.html">Python 自动化办公实战：Excel 处理 &#8211; 基础篇</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1453.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Java 基础语法入门：零基础也能看懂的详细教程</title>
		<link>https://www.dixunblog.cn/1287.html</link>
					<comments>https://www.dixunblog.cn/1287.html#respond</comments>
		
		<dc:creator><![CDATA[小编]]></dc:creator>
		<pubDate>Thu, 26 Mar 2026 16:37:35 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Windows教程]]></category>
		<category><![CDATA[代码教程]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/1287.html</guid>

					<description><![CDATA[<p>Java 作为最流行的编程语言之一，广泛应用于企业开发、Android 应用、大数据等领域。本文从零开始讲解 Java 基础语法，包括变量、数据类型、运算符、控制流程、数组、方法、面向对象等核心概念，适合零基础初学者。</p>
<p><a href="https://www.dixunblog.cn/1287.html">Java 基础语法入门：零基础也能看懂的详细教程</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>Java 作为最流行的编程语言之一，广泛应用于企业开发、Android 应用、大数据等领域。本文将带你从零开始学习 Java 基础语法。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1325" src="http://cdn.hyclive.cn/dixunblog/2026/03/Java-基础语法入门.png" alt="" width="2730" height="1535" /></p>
<h2>一、第一个 Java 程序</h2>
<pre><code class="language-java">public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}</code></pre>
<p>运行结果：</p>
<pre><code>Hello, Java!</code></pre>
<h3>代码解析：</h3>
<ul>
<li><code>public class</code>：定义公共类，类名必须与文件名一致</li>
<li><code>main 方法</code>：程序入口，JVM 从这里开始执行</li>
<li><code>System.out.println()</code>：控制台输出语句</li>
</ul>
<h2>二、变量与数据类型</h2>
<h3>1. 基本数据类型</h3>
<pre><code class="language-java">public class DataType {
    public static void main(String[] args) {
        // 整数类型
        int age = 25;
        long population = 7_000_000_000L;
        
        // 浮点类型
        double price = 99.99;
        float pi = 3.14f;
        
        // 字符类型
        char grade = 'A';
        
        // 布尔类型
        boolean isJavaFun = true;
        
        System.out.println("年龄：" + age);
        System.out.println("价格：" + price);
        System.out.println("等级：" + grade);
        System.out.println("有趣吗：" + isJavaFun);
    }
}</code></pre>
<h3>2. 引用数据类型</h3>
<pre><code class="language-java">String name = "张三";
String city = "北京";

System.out.println(name + "住在" + city);</code></pre>
<h2>三、运算符</h2>
<h3>1. 算术运算符</h3>
<pre><code class="language-java">int a = 10, b = 3;

System.out.println("a + b = " + (a + b));  // 13
System.out.println("a - b = " + (a - b));  // 7
System.out.println("a * b = " + (a * b));  // 30
System.out.println("a / b = " + (a / b));  // 3 (整数除法)
System.out.println("a % b = " + (a % b));  // 1 (取余)</code></pre>
<h3>2. 关系运算符</h3>
<pre><code class="language-java">int x = 5, y = 10;

System.out.println("x == y: " + (x == y));  // false
System.out.println("x != y: " + (x != y));  // true
System.out.println("x &gt; y: " + (x &gt; y));    // false
System.out.println("x &lt; y: " + (x = 5: " + (x &gt;= 5));  // true</code></pre>
<h3>3. 逻辑运算符</h3>
<pre><code class="language-java">boolean a = true, b = false;

System.out.println("a &amp;&amp; b: " + (a &amp;&amp; b));  // false (与)
System.out.println("a || b: " + (a || b));  // true (或)
System.out.println("!a: " + (!a));          // false (非)</code></pre>
<h2>四、控制流程</h2>
<h3>1. if-else 条件语句</h3>
<pre><code class="language-java">import java.util.Scanner;

public class IfExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入你的年龄：");
        int age = scanner.nextInt();
        
        if (age &lt; 18) {
            System.out.println("未成年");
        } else if (age &lt; 60) {
            System.out.println("成年人");
        } else {
            System.out.println("老年人");
        }
        
        scanner.close();
    }
}</code></pre>
<h3>2. switch 语句</h3>
<pre><code class="language-java">public class SwitchExample {
    public static void main(String[] args) {
        int day = 3;
        String dayName;
        
        switch (day) {
            case 1:
                dayName = "星期一";
                break;
            case 2:
                dayName = "星期二";
                break;
            case 3:
                dayName = "星期三";
                break;
            case 4:
                dayName = "星期四";
                break;
            case 5:
                dayName = "星期五";
                break;
            case 6:
            case 7:
                dayName = "周末";
                break;
            default:
                dayName = "无效的日期";
        }
        
        System.out.println("今天是：" + dayName);
    }
}</code></pre>
<h3>3. for 循环</h3>
<pre><code class="language-java">public class ForLoop {
    public static void main(String[] args) {
        // 计算 1 到 100 的和
        int sum = 0;
        for (int i = 1; i &lt;= 100; i++) {
            sum += i;
        }
        System.out.println("1 到 100 的和：" + sum);
        
        // 打印 99 乘法表
        for (int i = 1; i &lt;= 9; i++) {
            for (int j = 1; j &lt;= i; j++) {
                System.out.print(j + "×" + i + "=" + (i*j) + "  ");
            }
            System.out.println();
        }
    }
}</code></pre>
<h3>4. while 循环</h3>
<pre><code class="language-java">public class WhileLoop {
    public static void main(String[] args) {
        int count = 1;
        
        while (count &lt;= 5) {
            System.out.println("第 " + count + " 次循环");
            count++;
        }
        
        // do-while 循环
        int num = 1;
        do {
            System.out.println("至少执行一次：" + num);
            num++;
        } while (num &lt;= 3);
    }
}</code></pre>
<h2>五、数组</h2>
<pre><code class="language-java">public class ArrayExample {
    public static void main(String[] args) {
        // 声明并初始化数组
        int[] numbers = {1, 2, 3, 4, 5};
        
        // 访问数组元素
        System.out.println("第一个元素：" + numbers[0]);
        System.out.println("数组长度：" + numbers.length);
        
        // 遍历数组
        System.out.print("所有元素：");
        for (int i = 0; i &lt; numbers.length; i++) {
            System.out.print(numbers[i] + " ");
        }
        System.out.println();
        
        // 增强型 for 循环
        System.out.print("增强 for：");
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        
        // 二维数组
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        
        System.out.println("\n二维数组：");
        for (int[] row : matrix) {
            for (int val : row) {
                System.out.print(val + " ");
            }
            System.out.println();
        }
    }
}</code></pre>
<h2>六、方法（函数）</h2>
<pre><code class="language-java">public class MethodExample {
    
    // 无返回值方法
    public static void sayHello(String name) {
        System.out.println("你好，" + name + "！");
    }
    
    // 有返回值方法
    public static int add(int a, int b) {
        return a + b;
    }
    
    // 可变参数
    public static int sum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }
    
    // 递归方法：计算阶乘
    public static long factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        return n * factorial(n - 1);
    }
    
    public static void main(String[] args) {
        sayHello("张三");
        
        int result = add(10, 20);
        System.out.println("10 + 20 = " + result);
        
        System.out.println("1+2+3+4+5 = " + sum(1, 2, 3, 4, 5));
        
        System.out.println("5 的阶乘 = " + factorial(5));
    }
}</code></pre>
<h2>七、面向对象基础</h2>
<h3>1. 类的定义</h3>
<pre><code class="language-java">// 定义一个学生类
class Student {
    // 成员变量
    String name;
    int age;
    String major;
    
    // 构造方法
    public Student(String name, int age, String major) {
        this.name = name;
        this.age = age;
        this.major = major;
    }
    
    // 成员方法
    public void study() {
        System.out.println(name + "正在学习" + major + "专业");
    }
    
    public void introduce() {
        System.out.println("我叫" + name + "，今年" + age + "岁");
    }
}

// 使用类
public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("李四", 20, "计算机科学与技术");
        Student s2 = new Student("王五", 21, "软件工程");
        
        s1.introduce();
        s1.study();
        
        s2.introduce();
        s2.study();
    }
}</code></pre>
<h2>八、实战练习</h2>
<h3>练习 1：猜数字游戏</h3>
<pre><code class="language-java">import java.util.Random;
import java.util.Scanner;

public class GuessNumber {
    public static void main(String[] args) {
        Random random = new Random();
        int target = random.nextInt(100) + 1;
        Scanner scanner = new Scanner(System.in);
        int attempts = 0;
        
        System.out.println("=== 猜数字游戏 ===");
        System.out.println("我已经想好了一个 1-100 之间的数字");
        
        while (true) {
            System.out.print("请猜：");
            int guess = scanner.nextInt();
            attempts++;
            
            if (guess  target) {
                System.out.println("太大了！");
            } else {
                System.out.println("恭喜你猜对了！");
                System.out.println("总共猜了 " + attempts + " 次");
                break;
            }
        }
        
        scanner.close();
    }
}</code></pre>
<h2>总结</h2>
<p>本文涵盖了 Java 基础语法的核心内容：</p>
<ol>
<li>✅ 变量与数据类型</li>
<li>✅ 运算符（算术、关系、逻辑）</li>
<li>✅ 控制流程（if、switch、for、while）</li>
<li>✅ 数组（一维、二维）</li>
<li>✅ 方法定义与调用</li>
<li>✅ 面向对象基础（类与对象）</li>
</ol>
<h3>学习建议：</h3>
<ul>
<li><strong>多敲代码</strong>：把示例代码都亲手敲一遍</li>
<li><strong>多练习</strong>：完成文中的实战练习</li>
<li><strong>理解原理</strong>：不要死记硬背，理解为什么这样写</li>
<li><strong>循序渐进</strong>：基础打牢后再学习高级特性</li>
</ul>
<hr />
<p><strong>下节预告</strong>：《Java 面向对象进阶：继承、封装、多态》</p>
<p><em>如果觉得本文有帮助，欢迎收藏、转发，让更多人看到！有任何问题欢迎在评论区留言讨论。</em></p>
<p><a href="https://www.dixunblog.cn/1287.html">Java 基础语法入门：零基础也能看懂的详细教程</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1287.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>C 语言指针详解：从入门到精通的完整指南</title>
		<link>https://www.dixunblog.cn/1286.html</link>
					<comments>https://www.dixunblog.cn/1286.html#respond</comments>
		
		<dc:creator><![CDATA[小编]]></dc:creator>
		<pubDate>Thu, 26 Mar 2026 16:34:08 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/1286.html</guid>

					<description><![CDATA[<p>指针是 C 语言的灵魂，也是很多初学者最头疼的概念。本文通过大量实例，带你彻底理解 C 语言指针的核心用法，包括指针基础、数组与指针、函数指针、动态内存分配等。</p>
<p><a href="https://www.dixunblog.cn/1286.html">C 语言指针详解：从入门到精通的完整指南</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>指针是 C 语言的灵魂，也是很多初学者最头疼的概念。本文将通过大量实例，带你彻底理解 C 语言指针的核心用法。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1322" src="http://cdn.hyclive.cn/dixunblog/2026/03/C-语言指针主题.png" alt="" width="2730" height="1535" /></p>
<h2>什么是指针？</h2>
<p>简单来说，指针就是一个变量，它存储的是另一个变量的内存地址。想象一下，你家的地址就是一个&#8221;指针&#8221;，通过这个地址可以找到你家。</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int main() {
    int age = 25;        // 定义一个整型变量
    int *p = &amp;age;       // p 是指针，存储 age 的地址
    
    printf("age 的值：%d\n", age);
    printf("age 的地址：%p\n", &amp;age);
    printf("指针 p 的值：%p\n", p);
    printf("指针 p 指向的值：%d\n", *p);
    
    return 0;
}</code></pre>
<p>输出结果：</p>
<pre><code>age 的值：25
age 的地址：0x7fff5fbff6ac
指针 p 的值：0x7fff5fbff6ac
指针 p 指向的值：25</code></pre>
<h2>指针的核心运算符</h2>
<h3>1. 取地址运算符 &amp;</h3>
<pre><code class="language-c">int a = 10;
int *ptr = &amp;a;  // &amp;a 获取变量 a 的地址</code></pre>
<h3>2. 解引用运算符 *</h3>
<pre><code class="language-c">int a = 10;
int *ptr = &amp;a;
printf("%d", *ptr);  // *ptr 获取 ptr 指向的值，输出 10</code></pre>
<h2>指针与数组</h2>
<p>指针和数组有着密切的关系，数组名本质上就是一个常量指针。</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;  // ptr 指向数组第一个元素
    
    // 两种访问方式等价
    printf("arr[0] = %d\n", arr[0]);
    printf("*ptr = %d\n", *ptr);
    
    // 指针算术运算
    for(int i = 0; i &lt; 5; i++) {
        printf("arr[%d] = %d\n", i, *(ptr + i));
    }
    
    return 0;
}</code></pre>
<h2>指针与函数</h2>
<h3>1. 指针作为函数参数</h3>
<p>通过指针可以实现&#8221;传地址调用&#8221;，在函数内部修改外部变量的值。</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

// 交换两个数的值
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&amp;x, &amp;y);
    printf("x = %d, y = %d\n", x, y);  // 输出：x = 20, y = 10
    return 0;
}</code></pre>
<h3>2. 返回指针的函数</h3>
<pre><code class="language-c">// 返回数组中最大值的指针
int* findMax(int arr[], int size) {
    int *maxPtr = &amp;arr[0];
    for(int i = 1; i  *maxPtr) {
            maxPtr = &amp;arr[i];
        }
    }
    return maxPtr;
}</code></pre>
<h2>二级指针</h2>
<p>指针本身也是变量，也有地址，指向指针的指针就是二级指针。</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int main() {
    int a = 100;
    int *p1 = &amp;a;      // 一级指针
    int **p2 = &amp;p1;    // 二级指针
    
    printf("a = %d\n", a);
    printf("*p1 = %d\n", *p1);
    printf("**p2 = %d\n", **p2);
    
    return 0;
}</code></pre>
<h2>动态内存分配</h2>
<p>指针最重要的应用之一就是动态内存管理。</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int main() {
    // 动态分配数组
    int n = 5;
    int *arr = (int*)malloc(n * sizeof(int));
    
    // 使用动态数组
    for(int i = 0; i &lt; n; i++) {
        arr[i] = (i + 1) * 10;
    }
    
    // 打印结果
    for(int i = 0; i &lt; n; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    // 释放内存
    free(arr);
    arr = NULL;  // 避免野指针
    
    return 0;
}</code></pre>
<h2>常见指针错误</h2>
<h3>1. 野指针</h3>
<pre><code class="language-c">// 错误示例
int *ptr;  // 未初始化的指针
*ptr = 10; // 危险！ptr 指向未知地址

// 正确做法
int *ptr = NULL;  // 初始化为 NULL
int a = 10;
ptr = &amp;a;         // 再赋值有效地址</code></pre>
<h3>2. 内存泄漏</h3>
<pre><code class="language-c">// 错误示例
int *ptr = (int*)malloc(sizeof(int));
*ptr = 10;
// 忘记 free(ptr)，导致内存泄漏

// 正确做法
int *ptr = (int*)malloc(sizeof(int));
*ptr = 10;
// 使用完毕后
free(ptr);
ptr = NULL;</code></pre>
<h3>3. 悬空指针</h3>
<pre><code class="language-c">// 错误示例
int *getPointer() {
    int local = 100;
    return &amp;local;  // 错误！返回局部变量地址
}

// 正确做法
int *getPointer() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    return ptr;  // 返回动态分配的内存
}</code></pre>
<h2>实战练习</h2>
<h3>题目 1：字符串反转</h3>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

void reverseString(char *str) {
    char *start = str;
    char *end = str + strlen(str) - 1;
    char temp;
    
    while(start &lt; end) {
        temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}

int main() {
    char str[] = "Hello, World!";
    printf("原字符串：%s\n", str);
    reverseString(str);
    printf("反转后：%s\n", str);
    return 0;
}</code></pre>
<h3>题目 2：冒泡排序（指针实现）</h3>
<pre><code class="language-c">#include &lt;stdio.h&gt;

void bubbleSort(int *arr, int n) {
    for(int i = 0; i &lt; n - 1; i++) {
        for(int j = 0; j  *(arr + j + 1)) {
                int temp = *(arr + j);
                *(arr + j) = *(arr + j + 1);
                *(arr + j + 1) = temp;
            }
        }
    }
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    bubbleSort(arr, n);
    
    printf("排序后：");
    for(int i = 0; i &lt; n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}</code></pre>
<h2>总结</h2>
<p>指针是 C 语言的核心，掌握指针需要大量实践。以下是学习建议：</p>
<ol>
<li><strong>理解概念</strong>：指针就是地址，*和&amp;是核心运算符</li>
<li><strong>多画图</strong>：画出内存图帮助理解指针指向</li>
<li><strong>多练习</strong>：通过实际编程加深理解</li>
<li><strong>注意内存管理</strong>：malloc 和 free 配对使用</li>
<li><strong>避免常见错误</strong>：野指针、内存泄漏、悬空指针</li>
</ol>
<p>指针虽然难，但一旦掌握，你的 C 语言水平会有质的飞跃。加油！💪</p>
<hr />
<p><strong>下节预告</strong>：《数据结构基础：链表与指针的完美结合》</p>
<p><em>如果你觉得本文有帮助，欢迎收藏、转发，让更多人看到！</em></p>
<p><a href="https://www.dixunblog.cn/1286.html">C 语言指针详解：从入门到精通的完整指南</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1286.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Docker容器化部署全攻略：从零到企业级应用</title>
		<link>https://www.dixunblog.cn/1157.html</link>
					<comments>https://www.dixunblog.cn/1157.html#respond</comments>
		
		<dc:creator><![CDATA[老大]]></dc:creator>
		<pubDate>Sun, 15 Feb 2026 06:32:17 +0000</pubDate>
				<category><![CDATA[技术教程]]></category>
		<category><![CDATA[编程代码]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[容器]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/?p=1157</guid>

					<description><![CDATA[<p><a href="https://www.dixunblog.cn/1157.html">Docker容器化部署全攻略：从零到企业级应用</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p

<h2><strong><b>前言</b></strong></h2>
<p>容器化技术已经成为现代应用部署的标准。随着微服务架构的普及和云原生技术的发展，Docker作为容器化的事实标准，已经深入到每个开发者的日常工作中。本文将带你从Docker基础概念开始，逐步构建企业级容器化部署方案，无论你是初学者还是有一定经验的开发者，都能从中获得实用的知识和技巧。</p>
<p>&nbsp;</p>
<h2><strong><b>第一章：Docker基础概念与安装</b></strong></h2>
<h3><strong><b>1.1 什么是Docker？</b></strong></h3>
<p>Docker是一个开源的应用容器引擎，让开发者可以打包他们的应用以及依赖包到一个可移植的容器中，然后发布到任何流行的Linux机器或Windows机器上，也可以实现虚拟化。</p>
<p>&nbsp;</p>
<p><strong><b>核心概念：</b></strong></p>
<ul>
<li><b></b><strong><b>镜像（Image）</b></strong>：只读模板，包含运行应用程序所需的所有内容</li>
<li><b></b><strong><b>容器（Container）</b></strong>：镜像的运行实例，可以启动、停止、删除</li>
<li><b></b><strong><b>仓库（Registry）</b></strong>：存储和分发镜像的地方，如Docker Hub</li>
</ul>
<p>&nbsp;</p>
<h3><strong><b>1.2 Docker安装与配置</b></strong></h3>
<h4><strong><b>Ubuntu/Debian系统</b></strong></h4>
<p><img src="https://cdn.hyclive.cn/dixunblog/2026/02/aiimg_699168a9e795f2.79572023.webp" class="attachment-full size-full" alt="Docker容器化部署全攻略：从零到企业级应用" decoding="async" loading="lazy" /></p>
<pre>         
    # 更新包索引    
    sudo apt update    
         
    # 安装必要依赖    
    sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release    
         
    # 添加Docker官方GPG密钥    
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg    
         
    # 设置稳定版仓库    
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null    
         
    # 安装Docker Engine    
    sudo apt update    
    sudo apt install docker-ce docker-ce-cli containerd.io    
         
    # 验证安装    
    sudo docker run hello-world    
</pre>
<p>&nbsp;</p>
<h4><strong><b>CentOS/RHEL系统</b></strong></h4>
<pre>         
    # 卸载旧版本    
    sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine    
         
    # 安装必要工具    
    sudo yum install -y yum-utils    
         
    # 添加Docker仓库    
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo    
         
    # 安装Docker Engine    
    sudo yum install docker-ce docker-ce-cli containerd.io    
         
    # 启动并设置开机自启    
    sudo systemctl start docker    
    sudo systemctl enable docker    
</pre>
<p>&nbsp;</p>
<h4><strong><b>Windows WSL2</b></strong></h4>
<pre>         
    # 在PowerShell中以管理员身份运行    
    wsl --install    
    # 重启计算机后，安装Docker Desktop for Windows    
    # 确保WSL2后端已启用    
</pre>
<p>&nbsp;</p>
<h3><strong><b>1.3 用户权限配置</b></strong></h3>
<p>为了避免每次使用Docker命令都需要sudo，将当前用户添加到docker组：</p>
<pre>         
    sudo usermod -aG docker $USER    
    # 重新登录或执行以下命令    
    newgrp docker    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第二章：镜像构建最佳实践</b></strong></h2>
<h3><strong><b>2.1 Dockerfile编写规范</b></strong></h3>
<p><strong><b>基础Dockerfile示例：</b></strong></p>
<pre>         
    # 使用官方Node.js运行时作为基础镜像    
    FROM node:18-alpine    
         
    # 设置工作目录    
    WORKDIR /app    
         
    # 复制package.json和package-lock.json（如果存在）    
    COPY package*.json ./    
         
    # 安装依赖    
    RUN npm ci --only=production &amp;&amp; npm cache clean --force    
         
    # 复制应用源代码    
    COPY . .    
         
    # 暴露端口    
    EXPOSE 3000    
         
    # 定义环境变量    
    ENV NODE_ENV=production    
         
    # 运行应用    
    CMD ["node", "server.js"]    
</pre>
<p>&nbsp;</p>
<h3><strong><b>2.2 多阶段构建（Multi-stage Builds）</b></strong></h3>
<p>多阶段构建可以显著减小最终镜像的大小：</p>
<p>&nbsp;</p>
<pre>         
    # 构建阶段    
    FROM node:18-alpine AS builder    
    WORKDIR /app    
    COPY package*.json ./    
    RUN npm ci --only=production &amp;&amp; npm cache clean --force    
    COPY . .    
    RUN npm run build    
         
    # 运行阶段    
    FROM node:18-alpine AS runner    
    WORKDIR /app    
    COPY --from=builder /app/dist ./dist    
    COPY --from=builder /app/node_modules ./node_modules    
    COPY package*.json ./    
    EXPOSE 3000    
    CMD ["node", "dist/server.js"]    
</pre>
<p>&nbsp;</p>
<h3><strong><b>2.3 镜像优化技巧</b></strong></h3>
<p><strong><b>减少镜像层数：</b></strong></p>
<pre>         
    # 不好的做法 - 多个RUN指令    
    RUN apt-get update    
    RUN apt-get install -y curl    
    RUN apt-get install -y vim    
         
    # 好的做法 - 合并RUN指令    
    RUN apt-get update &amp;&amp;     
        apt-get install -y curl vim &amp;&amp;     
        rm -rf /var/lib/apt/lists/*    
</pre>
<p>&nbsp;</p>
<p><strong><b>使用.dockerignore文件：</b></strong></p>
<p>node_modules<br />
npm-debug.log<br />
.git<br />
.gitignore<br />
README.md<br />
.env<br />
.nyc_output<br />
coverage<br />
.nyc_output<br />
.coverage<br />
.vscode<br />
&nbsp;</p>
<h2><strong><b>第三章：Docker Compose编排多容器应用</b></strong></h2>
<h3><strong><b>3.1 Docker Compose基础</b></strong></h3>
<p>Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。</p>
<p>&nbsp;</p>
<p><strong><b>docker-compose.yml示例：</b></strong></p>
<pre>         
    version: '3.8'    
         
    services:    
      web:    
        build: .    
        ports:    
          - "3000:3000"    
        environment:    
          - NODE_ENV=production    
          - DATABASE_URL=postgresql://user:password@db:5432/myapp    
        depends_on:    
          - db    
          - redis    
        volumes:    
          - ./logs:/app/logs    
         
      db:    
        image: postgres:13    
        environment:    
          POSTGRES_DB: myapp    
          POSTGRES_USER: user    
          POSTGRES_PASSWORD: password    
        volumes:    
          - postgres_data:/var/lib/postgresql/data    
        restart: unless-stopped    
         
      redis:    
        image: redis:6-alpine    
        command: redis-server --appendonly yes    
        volumes:    
          - redis_data:/data    
        restart: unless-stopped    
         
    volumes:    
      postgres_data:    
      redis_data:    
</pre>
<p>&nbsp;</p>
<h3><strong><b>3.2 环境配置管理</b></strong></h3>
<p><strong><b>多环境配置：</b></strong></p>
<pre>         
    # docker-compose.prod.yml    
    version: '3.8'    
    services:    
      web:    
        build:    
          context: .    
          dockerfile: Dockerfile.prod    
        ports:    
          - "80:3000"    
        environment:    
          - NODE_ENV=production    
        restart: unless-stopped    
         
    # docker-compose.dev.yml      
    version: '3.8'    
    services:    
      web:    
        build:    
          context: .    
          dockerfile: Dockerfile.dev    
        ports:    
          - "3000:3000"    
        environment:    
          - NODE_ENV=development    
        volumes:    
          - .:/app    
          - /app/node_modules    
</pre>
<p>&nbsp;</p>
<p><strong><b>使用命令：</b></strong></p>
<pre>         
    # 开发环境    
    docker-compose -f docker-compose.yml -f docker-compose.dev.yml up    
         
    # 生产环境    
    docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第四章：网络配置与数据持久化</b></strong></h2>
<h3><strong><b>4.1 Docker网络模式</b></strong></h3>
<p><strong><b>四种网络模式：</b></strong></p>
<ol>
<li><strong><b>bridge</b></strong>（默认）：容器间通过虚拟网桥通信</li>
<li><strong><b>host</b></strong>：容器直接使用主机网络</li>
<li><strong><b>none</b></strong>：容器无网络</li>
<li><strong><b>overlay</b></strong>：跨主机容器通信</li>
</ol>
<p>&nbsp;</p>
<p><strong><b>自定义网络：</b></strong></p>
<pre>         
    # 创建自定义网络    
    docker network create --driver bridge my-network    
         
    # 运行容器时指定网络    
    docker run --network my-network --name web-app nginx    
</pre>
<p>&nbsp;</p>
<h3><strong><b>4.2 数据持久化策略</b></strong></h3>
<p><strong><b>三种数据持久化方式：</b></strong></p>
<p>&nbsp;</p>
<ol>
<li><strong><b>Volumes（推荐）</b></strong></li>
</ol>
<pre>         
    # 创建命名卷    
    docker volume create my-vol    
         
    # 挂载卷到容器    
    docker run -d --name web-app -v my-vol:/app/data nginx    
</pre>
<p>&nbsp;</p>
<ol start="2">
<li><strong><b>Bind Mounts</b></strong></li>
</ol>
<pre>         
    # 挂载主机目录    
    docker run -d --name web-app -v /host/path:/container/path nginx    
</pre>
<p>&nbsp;</p>
<ol start="3">
<li><strong><b>tmpfs Mounts</b></strong></li>
</ol>
<pre>         
    # 临时文件系统挂载（仅Linux）    
    docker run -d --name web-app --tmpfs /app/cache nginx    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第五章：安全加固与资源限制</b></strong></h2>
<h3><strong><b>5.1 安全最佳实践</b></strong></h3>
<p><strong><b>使用非root用户：</b></strong></p>
<pre>         
    FROM node:18-alpine    
    RUN addgroup -g 1001 -S nodejs    
    RUN adduser -S nextjs -u 1001    
    USER nextjs    
</pre>
<p>&nbsp;</p>
<p><strong><b>扫描镜像漏洞：</b></strong></p>
<pre>         
    # 使用Docker Scout    
    docker scout quickview your-image:tag    
         
    # 使用Trivy    
    trivy image your-image:tag    
</pre>
<p>&nbsp;</p>
<p><strong><b>最小化基础镜像：</b></strong></p>
<ul>
<li>优先使用alpine版本</li>
<li>使用distroless镜像</li>
<li>定期更新基础镜像</li>
</ul>
<p>&nbsp;</p>
<h3><strong><b>5.2 资源限制</b></strong></h3>
<p><strong><b>CPU和内存限制：</b></strong></p>
<pre>         
    # docker-compose.yml    
    services:    
      web:    
        image: nginx    
        deploy:    
          resources:    
            limits:    
              cpus: '0.5'    
              memory: 512M    
            reservations:    
              cpus: '0.25'    
              memory: 256M    
</pre>
<p>&nbsp;</p>
<p><strong><b>单独容器的资源限制：</b></strong></p>
<pre>         
    docker run -d     
      --name web-app     
      --memory=512m     
      --cpus=0.5     
      nginx    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第六章：CI/CD集成方案</b></strong></h2>
<h3><strong><b>6.1 GitHub Actions集成</b></strong></h3>
<p><strong><b>.github/workflows/docker-build.yml：</b></strong></p>
<pre>         
    name: Build and Push Docker Image    
         
    on:    
      push:    
        branches: [ main ]    
         
    jobs:    
      build:    
        runs-on: ubuntu-latest    
            
        steps:    
        - uses: actions/checkout@v3    
            
        - name: Set up Docker Buildx    
          uses: docker/setup-buildx-action@v2    
              
        - name: Login to Docker Hub    
          uses: docker/login-action@v2    
          with:    
            username: ${{ secrets.DOCKERHUB_USERNAME }}    
            password: ${{ secrets.DOCKERHUB_TOKEN }}    
                
        - name: Build and push    
          uses: docker/build-push-action@v4    
          with:    
            context: .    
            push: true    
            tags: your-username/your-app:latest    
</pre>
<p>&nbsp;</p>
<h3><strong><b>6.2 GitLab CI/CD集成</b></strong></h3>
<p><strong><b>.gitlab-ci.yml：</b></strong></p>
<pre>         
    stages:    
      - build    
      - test    
      - deploy    
         
    build-image:    
      stage: build    
      script:    
        - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .    
        - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA    
         
    deploy-production:    
      stage: deploy    
      script:    
        - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY    
        - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA    
        - docker stop my-app || true    
        - docker rm my-app || true    
        - docker run -d --name my-app -p 80:3000 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA    
      only:    
        - main    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第七章：实战案例 &#8211; 完整Web应用部署</b></strong></h2>
<h3><strong><b>7.1 应用架构</b></strong></h3>
<p>我们将部署一个完整的Web应用，包含以下组件：</p>
<ul>
<li><b></b><strong><b>Nginx</b></strong>：反向代理和静态文件服务</li>
<li><b></b><strong><b>PHP-FPM</b></strong>：PHP应用服务器</li>
<li><b></b><strong><b>MySQL</b></strong>：数据库</li>
<li><b></b><strong><b>Redis</b></strong>：缓存和会话存储</li>
</ul>
<p>&nbsp;</p>
<h3><strong><b>7.2 目录结构</b></strong></h3>
<p>my-web-app/<br />
├── docker-compose.yml<br />
├── nginx/<br />
│   └── default.conf<br />
├── php/<br />
│   └── Dockerfile<br />
├── src/<br />
│   └── index.php<br />
└── .env<br />
&nbsp;</p>
<h3><strong><b>7.3 配置文件</b></strong></h3>
<p><strong><b>docker-compose.yml：</b></strong></p>
<pre>         
    version: '3.8'    
         
    services:    
      nginx:    
        image: nginx:alpine    
        ports:    
          - "80:80"    
        volumes:    
          - ./src:/var/www/html    
          - ./nginx/default.conf:/etc/nginx/conf.d/default.conf    
        depends_on:    
          - php    
        restart: unless-stopped    
         
      php:    
        build: ./php    
        volumes:    
          - ./src:/var/www/html    
        environment:    
          - DB_HOST=mysql    
          - DB_NAME=myapp    
          - DB_USER=user    
          - DB_PASS=password    
        depends_on:    
          - mysql    
          - redis    
        restart: unless-stopped    
         
      mysql:    
        image: mysql:8.0    
        environment:    
          MYSQL_DATABASE: myapp    
          MYSQL_USER: user    
          MYSQL_PASSWORD: password    
          MYSQL_ROOT_PASSWORD: rootpassword    
        volumes:    
          - mysql_data:/var/lib/mysql    
        restart: unless-stopped    
         
      redis:    
        image: redis:6-alpine    
        volumes:    
          - redis_data:/data    
        restart: unless-stopped    
         
    volumes:    
      mysql_data:    
      redis_data:    
</pre>
<p>&nbsp;</p>
<p><strong><b>nginx/default.conf：</b></strong></p>
<pre>         
    server {    
        listen 80;    
        server_name localhost;    
        root /var/www/html;    
        index index.php;    
         
        location / {    
            try_files $uri $uri/ /index.php?$query_string;    
        }    
         
        location ~ .php$ {    
            fastcgi_pass php:9000;    
            fastcgi_index index.php;    
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;    
            include fastcgi_params;    
        }    
    }    
</pre>
<p>&nbsp;</p>
<p><strong><b>php/Dockerfile：</b></strong></p>
<pre>         
    FROM php:8.1-fpm-alpine    
         
    # 安装扩展    
    RUN docker-php-ext-install mysqli pdo pdo_mysql    
         
    # 安装Redis扩展    
    RUN pecl install redis &amp;&amp; docker-php-ext-enable redis    
         
    # 清理    
    RUN rm -rf /tmp/* /var/tmp/*    
         
    WORKDIR /var/www/html    
</pre>
<p>&nbsp;</p>
<p><strong><b>src/index.php：</b></strong></p>
<pre>         
    &lt;?php    
    echo "&lt;h1&gt;欢迎使用Docker容器化部署！&lt;/h1&gt;";    
         
    // 测试数据库连接    
    try {    
        $pdo = new PDO(    
            "mysql:host=mysql;dbname=myapp",    
            "user",    
            "password"    
        );    
        echo "&lt;p&gt;✅ 数据库连接成功！&lt;/p&gt;";    
    } catch (PDOException $e) {    
        echo "&lt;p&gt;❌ 数据库连接失败: " . $e-&gt;getMessage() . "&lt;/p&gt;";    
    }    
         
    // 测试Redis连接    
    try {    
        $redis = new Redis();    
        $redis-&gt;connect('redis', 6379);    
        $redis-&gt;set('test', 'Hello Redis!');    
        echo "&lt;p&gt;✅ Redis连接成功！&lt;/p&gt;";    
        echo "&lt;p&gt;Redis测试值: " . $redis-&gt;get('test') . "&lt;/p&gt;";    
    } catch (Exception $e) {    
        echo "&lt;p&gt;❌ Redis连接失败: " . $e-&gt;getMessage() . "&lt;/p&gt;";    
    }    
    ?&gt;    
</pre>
<p>&nbsp;</p>
<h3><strong><b>7.4 部署步骤</b></strong></h3>
<pre>         
    # 1. 克隆项目    
    git clone your-repo-url    
    cd my-web-app    
         
    # 2. 构建并启动服务    
    docker-compose up -d    
         
    # 3. 验证服务状态    
    docker-compose ps    
         
    # 4. 查看日志    
    docker-compose logs -f    
         
    # 5. 访问应用    
    # 打开浏览器访问 http://localhost    
</pre>
<p>&nbsp;</p>
<h2><strong><b>第八章：监控与维护</b></strong></h2>
<h3><strong><b>8.1 日志管理</b></strong></h3>
<p><strong><b>查看容器日志：</b></strong></p>
<pre>         
    # 查看特定服务日志    
    docker-compose logs web    
         
    # 实时跟踪日志    
    docker-compose logs -f web    
         
    # 查看最近100行日志    
    docker-compose logs --tail=100 web    
</pre>
<p>&nbsp;</p>
<h3><strong><b>8.2 性能监控</b></strong></h3>
<p><strong><b>使用cAdvisor监控容器性能：</b></strong></p>
<pre>         
    # docker-compose.monitoring.yml    
    version: '3.8'    
    services:    
      cadvisor:    
        image: gcr.io/cadvisor/cadvisor:v0.47.0    
        volumes:    
          - /:/rootfs:ro    
          - /var/run:/var/run:rw    
          - /sys:/sys:ro    
          - /var/lib/docker/:/var/lib/docker:ro    
        ports:    
          - "8080:8080"    
        privileged: true    
        devices:    
          - /dev/kmsg    
</pre>
<p>&nbsp;</p>
<h3><strong><b>8.3 定期维护任务</b></strong></h3>
<p><strong><b>清理未使用的资源：</b></strong></p>
<pre>         
    # 清理停止的容器    
    docker container prune    
         
    # 清理未使用的镜像    
    docker image prune -a    
         
    # 清理未使用的卷    
    docker volume prune    
         
    # 清理未使用的网络    
    docker network prune    
         
    # 一键清理所有未使用资源    
    docker system prune -a    
</pre>
<p>&nbsp;</p>
<h2><strong><b>结语</b></strong></h2>
<p>Docker容器化部署不仅简化了应用的部署流程，还提高了环境的一致性和可移植性。通过本文的学习，你应该已经掌握了从基础概念到企业级应用部署的完整知识体系。</p>
<p>&nbsp;</p>
<p>记住，容器化不是终点，而是现代化应用开发和运维的新起点。随着Kubernetes、Service Mesh等技术的发展，容器化生态还在不断演进。保持学习，持续优化你的容器化实践！</p>
<p>&nbsp;</p>
<h2><strong><b>附录：常用Docker命令速查</b></strong></h2>
<table>
<tbody>
<tr>
<td width="366"><strong><b>命令</b></strong></td>
<td width="257"><strong><b>说明</b></strong></td>
</tr>
<tr>
<td width="366">docker ps</td>
<td width="257">列出运行中的容器</td>
</tr>
<tr>
<td width="366">docker ps -a</td>
<td width="257">列出所有容器</td>
</tr>
<tr>
<td width="366">docker images</td>
<td width="257">列出本地镜像</td>
</tr>
<tr>
<td width="366">docker build -t name:tag .</td>
<td width="257">构建镜像</td>
</tr>
<tr>
<td width="366">docker run -d &#8211;name container-name image</td>
<td width="257">运行容器</td>
</tr>
<tr>
<td width="366">docker exec -it container-name bash</td>
<td width="257">进入容器</td>
</tr>
<tr>
<td width="366">docker logs container-name</td>
<td width="257">查看容器日志</td>
</tr>
<tr>
<td width="366">docker-compose up -d</td>
<td width="257">启动所有服务</td>
</tr>
<tr>
<td width="366">docker-compose down</td>
<td width="257">停止并删除所有服务</td>
</tr>
<tr>
<td width="366">docker system df</td>
<td width="257">查看Docker磁盘使用情况</td>
</tr>
</tbody>
</table>
<p><a href="https://www.dixunblog.cn/1157.html">Docker容器化部署全攻略：从零到企业级应用</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1157.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>这些Java基础知识，诸佬们都还记得嘛(学习，复习，面试都可)</title>
		<link>https://www.dixunblog.cn/1026.html</link>
					<comments>https://www.dixunblog.cn/1026.html#respond</comments>
		
		<dc:creator><![CDATA[老大]]></dc:creator>
		<pubDate>Mon, 29 Dec 2025 08:58:21 +0000</pubDate>
				<category><![CDATA[编程代码]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[代码教程]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/?p=1026</guid>

					<description><![CDATA[<p>JVM线程私有和共享的区域 JVM线程私有的区域有：虚拟机栈，本地方法栈，程序计数器。 虚拟机栈：主要存储方法，局部变量，运行的数据。 本地方法栈：主要存储本地方法（含有Native关键字的方法）。 程序计数器：存储程序运行位置的字节码行号指示器。 JVM线程共享的区域有：Java堆，元空间 Java堆：存储所有创建的对象，数组等。 元空间：存储虚拟机加载的字节码数据，常量，静态变量，运行时常量池等。 线程上下文切换 线程上下文切换，也就是CPU不再执行当前的线程，而去执行其他的线程。那有哪些原因会导致线程的上下文</p>
<p><a href="https://www.dixunblog.cn/1026.html">这些Java基础知识，诸佬们都还记得嘛(学习，复习，面试都可)</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<h2 id="2j9bu">JVM线程私有和共享的区域</h2>
<p><strong>JVM线程私有的区域有：</strong><span class="mod-overview__keyword"><strong>虚拟机</strong></span><strong>栈，本地方法栈，程序计数器</strong>。</p>
<p><strong>虚拟机栈</strong>：主要存储方法，局部变量，运行的数据。 <strong>本地方法栈</strong>：主要存储本地方法（<strong>含有Native关键字的方法</strong>）。 <strong>程序计数器</strong>：存储程序运行位置的字节码行号指示器。</p>
<p><strong>JVM线程共享的区域有：</strong><strong>Java</strong><strong>堆，元空间</strong></p>
<p><span class="mod-overview__keyword"><strong>Java</strong></span><strong>堆</strong>：存储所有创建的对象，数组等。 <strong>元空间</strong>：存储虚拟机加载的字节码数据，常量，静态变量，运行时常量池等。</p>
<h2 id="dpqj">线程上下文切换</h2>
<p><strong>线程上下文切换，也就是CPU不再执行当前的线程，而去执行其他的线程</strong>。那有哪些原因会导致线程的上下文切换呢？</p>
<ol class="ol-level-0">
<li>线程的时间片用完</li>
<li>垃圾回收（会暂停当前工作的线程，先进行垃圾回收）</li>
<li>更高优先级的线程运行</li>
<li>线程主动调用了某些方法，如sleep，yeild，wait，join，synchronized，lock等</li>
</ol>
<p>当发生上下文切换时，操作系统会保存当前线程的状态，恢复另一个线程的状态，此时程序计数器会记住下一条jvm指令的执行地址，同时上文记录，<strong>程序计数器是线程私有的</strong>。</p>
<h2 id="cm987">如何判断对象是否存活</h2>
<p>判断对象是否存活有两种方法：<strong>引用计数算法和可达性分析算法</strong>。</p>
<h3 id="e1rcp">引用计数法</h3>
<p>在对象被创建的时候，会在对象头中分配一个空间，即计时器，来保存这个对象被引用的次数。如果这个对象被其他的对象引用，它的引用计数器会+1，如果删除其他对象对这个对象的引用，则它的引用计数会-1，当对象的引用计数为0时，这个对象就会被当成垃圾回收。</p>
<blockquote><p>优点： 引用计数法实现起来比较简单，判断对象是否存活的效率比较高。 缺点： 无法解决对象之间循环引用的问题，不能检测到环的出现。例如，A和B之间相互引用，此时计数器都会显示为1，此时A和B都无法进行垃圾回收。</p></blockquote>
<h3 id="6i7v0">可达性分析法</h3>
<p>Java虚拟机中的垃圾回收机制都是采用的可达性分析算法来探索存活的对象的。此种方法工作原理是会扫描java堆中的对象，沿着<strong>GC Roots对象</strong>往下寻找，看看是否能在此引用链中找到该对象，如果找不到的话，证明该对象没用了，表示该对象可以回收。</p>
<p><strong>可达性分析算法最大的优点之一就是解决了对象之间的相互循环依赖的问题，目前和引用计数法比起来没有缺点</strong>。</p>
<h2 id="3u2us">JVM中的垃圾回收算法</h2>
<p>对于新生代和老年代的对象，在JVM中会采取不同的垃圾回收算法。年轻代的对象一般都是朝生暮死的，创建之后很快就会被回收，而老年代的对象是需要长期存活的，因此用到的算法大不相同。新生代对应的收集方法为“Minor GC”，老年代对应的收集方法称为“Major GC”，而对于整个堆空间和方法区的回收被称为“Full GC”</p>
<p>&nbsp;</p>
<h3 id="15bgg">标记清除算法</h3>
<p>标记清除算法为最基础的垃圾收集算法，即为每个对象都分配一个标记为，这个标记位会记录对象的状态。标记着所要回收的对象，在标记完成后，统一回收掉所有被标记的对象，也可以标记存活的对象，清理掉未标记的对象。<strong>标记清除算法用于老年代的垃圾回收中</strong>。</p>
<blockquote><p>优点： 基于可达性分析算法，实现起来比较简单，后续的算法都是基于这种思想来实现的。 缺点： 影响最大的一点在于，<strong>标记清除算法会使内存空间碎片化</strong>，即标记并清除垃圾后，会产生很多不连续的内存空间，这将导致较大的对象因为无法找到连续的内存而提前触发一次垃圾回收。 <strong>如果大部分对象需要回收，就会进行大量的标记和清除操作，存活对象数量多时效率会降低</strong>。</p></blockquote>
<h3 id="66m77">复制算法</h3>
<p>复制算法被用于新生代的垃圾回收机制中，新生代有三部分，<strong>Eden（80%），和两个survivor区（From Survivor 和 To Survivor）</strong>。两个Survivor区为<strong>容量大小相等的两块内存</strong>，每次只使用其中的一块内存，当使用的那块内存用完后，就会将内存中还存活着的对象复制到另一块内存上，然后把使用过的那块内存空间清空。</p>
<blockquote><p>优点： 实现起来比较简单，效率也比较高，<strong>可以保证内存有连续的区域</strong>，<strong>能够解决标记清除算法导致的内存碎片问题。</strong> 缺点： 可分配的内存空间缩小了一半儿，<strong>代价比较高，内存空间浪费比较多</strong>； 存活的对象比较多的时候使用复制算法将会导致效率降低。 进行标记清除算法时，会导致应用程序挂起（停顿），即stop the world（STW）。</p></blockquote>
<p>扩展： 90%以上的对象都是朝生暮死的，所以在新生代中，每次为对象分配内存时会使用Eden区和其中的一块Survivor区，<strong>当发生垃圾回收时，JVM会将Eden和Survivor中存活的对象都复制到另一块Survivor区域内，之后清理掉Eden区和Survivor区域中的空间</strong>。综上所述，建立新对象时，新生代可用内存空间为整个儿新生代容量的90%（80%的Eden区和10%的Survivor区），如果发生了极少部分情况，即多于10%的对象存活下来了，没有被垃圾回收器回收掉，此时JVM会触发空间担保机制，即当Survivor空间不足以容纳一次Minor GC后的存活对象时，就需要依赖老年代进行分配担保。</p>
<h3 id="8611l">标记整理算法</h3>
<p>标记整理法是对标记清除算法的一个改进。第一个阶段和标记清除算法一样，都是将对象标记为存活和死亡状态，然而在第二阶段，标记清除算法只是将被标记的对象进行清除，标记整理算法会将存活的对象进行整理并且放到另一个端，然后再把所有的对象清除掉。 <strong>标记整理算法用于老年代的回收机制中</strong>。</p>
<blockquote><p>优点： 不会像垃圾清除算法那样产生不连续的内存碎片空间 不会像复制算法那样划分两个区域，提高了空间的利用率 缺点： 效率上肯定更慢一些，因为多了一步整理的操作过程。</p></blockquote>
<h2 id="dm5u5">如何判断变量是否线程安全</h2>
<p>对于成员变量和静态变量</p>
<ul class="ul-level-0">
<li>如果它们没有被共享，则它们是线程安全的；</li>
<li>如果它们被共享了，根据它们的状态是否能够改变，又会分两种情况：如果只有读操作，则它们是线程安全的；如果有读写操作，则这段代码是临界区，是需要考虑线程安全的。</li>
</ul>
<p>对于局部变量是否线程安全</p>
<ul class="ul-level-0">
<li>局部变量是线程安全的</li>
<li>但局部变量引用的对象则未必线程安全。如果该对象没有逃离方法的作用访问，它是线程安全的；如果该对象逃离方法的作用范围，则是需要考虑线程安全的。</li>
</ul>
<h2 id="5j3en">最长递增子序列</h2>
<p>给你一个整数数组 nums ，找到其中最长严格递增子序列的长度。</p>
<p>子序列 是由数组派生而来的序列，删除（或不删除）数组中的元素而不改变其余元素的顺序。例如，[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。</p>
<p>示例 1：</p>
<blockquote><p>输入：nums = [10,9,2,5,3,7,101,18] 输出：4 解释：最长递增子序列是 [2,3,7,101]，因此长度为 4 。</p>
<p>示例 2： 输入：nums = [0,1,0,3,2,3] 输出：4</p>
<p>示例 3： 输入：nums = [7,7,7,7,7,7,7] 输出：1</p></blockquote>
<p>思路：该题让求出最长的递增子序列，因此至少需要一次遍历，考虑到代码进行到每一步的状态才可以，所以动态规划法解决此题比较容易。</p>
<p>代码+详解：</p>
<div class="rno-markdown-code">
<div class="rno-markdown-code-toolbar">
<div class="rno-markdown-code-toolbar-info">
<div class="rno-markdown-code-toolbar-item"><span class="is-m-hidden">代码语言：</span>javascript</div>
</div>
<div class="rno-markdown-code-toolbar-opt">
<div class="rno-markdown-code-toolbar-opt-icon-btn is-ai-btn">
<div class="chat-ui-ai-logo css-2jdwfl"></div>
<p>AI代码解释</p>
</div>
</div>
</div>
<div class="developer-code-block">
<div class="rno-markdown-code-opt-float">
<div class="rno-markdown-code-icon-btn qa-r-editor-btn"></div>
</div>
<pre class="prism-token token line-numbers language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> int <span class="token function">lengthOfLIS</span><span class="token punctuation">(</span><span class="token parameter">int<span class="token punctuation">[</span><span class="token punctuation">]</span> nums</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>nums<span class="token punctuation">.</span>length<span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//长度为0直接返回0</span>
        <span class="token punctuation">}</span>
        int <span class="token punctuation">[</span><span class="token punctuation">]</span> dp<span class="token operator">=</span><span class="token keyword">new</span> <span class="token class-name">int</span><span class="token punctuation">[</span>nums<span class="token punctuation">.</span>length<span class="token punctuation">]</span><span class="token punctuation">;</span>
        dp<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">//初始化数组，也可以调用库函数Arrays.fill(dp,1)，但是效率会慢些</span>
        int result<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token comment">//初始化结果</span>
        <span class="token keyword">for</span><span class="token punctuation">(</span>int i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>nums<span class="token punctuation">.</span>length<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
            dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token comment">//初始化数组</span>
            <span class="token keyword">for</span><span class="token punctuation">(</span>int j<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>i<span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>nums<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token operator">&lt;</span>nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
                dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">=</span>Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span>dp<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//循环更新dp[i]的最大值</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
            result<span class="token operator">=</span>Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span>dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> result<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
</div>
</div>
<p><a href="https://www.dixunblog.cn/1026.html">这些Java基础知识，诸佬们都还记得嘛(学习，复习，面试都可)</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1026.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>小程序的组成结构</title>
		<link>https://www.dixunblog.cn/1023.html</link>
					<comments>https://www.dixunblog.cn/1023.html#respond</comments>
		
		<dc:creator><![CDATA[老大]]></dc:creator>
		<pubDate>Mon, 29 Dec 2025 08:55:33 +0000</pubDate>
				<category><![CDATA[编程代码]]></category>
		<category><![CDATA[小程序]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/?p=1023</guid>

					<description><![CDATA[<p>基本组成结构 （1）pages（目录）：用来存放所有小程序的页面。 （2）utils（目录）：用来存放工具性质的模板 （3）app.js:小程序项目的入口文件 （4）app.json:小程序项目的全局配置文件。 （5）app.wxss:小程序项目的全局样式文件 （6） project.config.json项目的配置文件 这是一个练习项目宿舍管理小程序，我们可以清晰地看到在文件夹miniprogram下存在的几个文件夹。 第一个文件夹images主要是存放一些小程序中所需的图片；</p>
<p><a href="https://www.dixunblog.cn/1023.html">小程序的组成结构</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<h2 id="h8vf">基本组成结构</h2>
<p>（1）<code>pages</code>（目录）：用来存放所有小程序的页面。</p>
<p>（2）<code>utils</code>（目录）：用来存放工具性质的模板</p>
<p>（3）<code>app.js</code>:小程序项目的入口文件</p>
<p>（4）<code>app.json</code>:小程序项目的全局配置文件。</p>
<p>（5）<code>app.wxss</code>:小程序项目的全局样式文件</p>
<p>（6）<code> project.config.json</code>项目的配置文件</p>
<figure class="">
<div class="rno-markdown-img-url">
<div class="rno-markdown-img-url-inner">
<div><img loading="lazy" decoding="async" class="size-full wp-image-1024 aligncenter" title="dd379a58ab27b7ee467418623ca7a674" src="http://cdn.hyclive.cn/dixunblog/2025/12/dd379a58ab27b7ee467418623ca7a674.png" alt="dd379a58ab27b7ee467418623ca7a674" width="570" height="296" /></div>
</div>
</div>
</figure>
<p>这是一个练习项目宿舍管理小程序，我们可以清晰地看到在文件夹miniprogram下存在的几个文件夹。 第一个文件夹images主要是存放一些小程序中所需的图片；</p>
<p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YqBTqrBc-1664789463320)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b41ad29e527442586288e704e3f7bba~tplv-k3u1fbpfcp-watermark.image?)]</p>
<p>pages就是存放小程序的每个页面，因为每个小程序肯定不会只有一个页面，所以pages下也存在多个页面。例如home是主页面，night是学生晚归管理界面等等。</p>
<p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ilBVlWH2-1664789463321)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b486a9199dff409dba11ede7c65b5d6f~tplv-k3u1fbpfcp-watermark.image?)]</p>
<h3 id="cdhd4">JSON配置</h3>
<blockquote><p><span class="mod-overview__keyword">JSON</span>是一种数据格式，在实际开发工作中，JSON总是以配置文件的形式存在。在小程序中它也是以配置文件的形式存在，通过对不同的.json配置文件进行设置，可以对小程序进行不同级别的设置。</p></blockquote>
<p>(1)小程序配置app.json</p>
<p>app.json是当前小程序的全局配置，包括了小程序的所有页面路径，窗口外观，界面表现等等。 新建一个空白的小程序，app.json默认的配置代码：</p>
<div class="rno-markdown-code">
<div class="rno-markdown-code-toolbar">
<div class="rno-markdown-code-toolbar-info">
<div class="rno-markdown-code-toolbar-item"><span class="is-m-hidden">代码语言：</span>javascript</div>
</div>
<div class="rno-markdown-code-toolbar-opt">
<div class="rno-markdown-code-toolbar-opt-icon-btn is-ai-btn">
<div class="chat-ui-ai-logo css-2jdwfl"></div>
<p>AI代码解释</p></div>
</div>
</div>
<div class="developer-code-block">
<div class="rno-markdown-code-opt-float">
<div class="rno-markdown-code-icon-btn qa-r-editor-btn"></div>
</div>
<pre class="prism-token token line-numbers language-javascript" tabindex="0"><code class="language-javascript"><span class="token punctuation">{</span>
    <span class="token string-property property">"pages"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
        <span class="token string">"pages/index/index"</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token string-property property">"window"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token string-property property">"backgroundTextStyle"</span><span class="token operator">:</span> <span class="token string">"light"</span><span class="token punctuation">,</span>
        <span class="token string-property property">"navigationBarBackgroundColor"</span><span class="token operator">:</span> <span class="token string">"#fff"</span><span class="token punctuation">,</span>
        <span class="token string-property property">"navigationBarTitleText"</span><span class="token operator">:</span> <span class="token string">"Weixin"</span><span class="token punctuation">,</span>
        <span class="token string-property property">"navigationBarTextStyle"</span><span class="token operator">:</span> <span class="token string">"black"</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token string-property property">"style"</span><span class="token operator">:</span> <span class="token string">"v2"</span><span class="token punctuation">,</span>
    <span class="token string-property property">"sitemapLocation"</span><span class="token operator">:</span> <span class="token string">"sitemap.json"</span>
<span class="token punctuation">}</span></code></pre>
</div>
</div>
<p>其中pages是用来记录所有页面的路径，window记录全局页面的背景和文字颜色；style定义了组件的样式版本；sitemapLocation用来指明sitemap.json的位置</p>
<p>（2） 工具配置project.config.json</p>
<p>小程序的每个项目根目录下都会生成一个工具配置文件，主要目的就是方便开发者根据自己的兴趣爱好进行一些个性化配置，比如界面颜色、编译配置等方面。你在开发者工具中进行的任何配置都是会写入到这个文件的。</p>
<p>（3）页面配置page.json</p>
<p>小程序中的每一个页面，可以使用.json文件来对本页面的窗口外观进行配置，页面中的配置项会覆盖app.json的window中相同的配置项。</p>
<h3 id="688go">WXML 模板</h3>
<p>其实小程序的开发和网页开发很类似，学过前端的小伙伴都知道网页是通过html+<span class="mod-overview__keyword">css</span>+js进行开发的。而在小程序中wxml充当的就是网页中html的角色。</p>
<div class="rno-markdown-code">
<div class="rno-markdown-code-toolbar">
<div class="rno-markdown-code-toolbar-info">
<div class="rno-markdown-code-toolbar-item"><span class="is-m-hidden">代码语言：</span>javascript</div>
</div>
<div class="rno-markdown-code-toolbar-opt">
<div class="rno-markdown-code-toolbar-opt-icon-btn is-ai-btn">
<div class="chat-ui-ai-logo css-2jdwfl"></div>
<p>AI代码解释</p></div>
</div>
</div>
<div class="developer-code-block">
<div class="rno-markdown-code-opt-float">
<div class="rno-markdown-code-icon-btn qa-r-editor-btn"></div>
</div>
<pre class="prism-token token line-numbers language-javascript" tabindex="0"><code class="language-javascript"><span class="token operator">&lt;</span>view <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"container"</span><span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span>view <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"userinfo"</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>button wx<span class="token operator">:</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"{{!hasUserInfo &amp;&amp; canIUse}}"</span><span class="token operator">&gt;</span> 获取头像昵称 <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>block wx<span class="token operator">:</span><span class="token keyword">else</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>image src<span class="token operator">=</span><span class="token string">"{{userInfo.avatarUrl}}"</span> background<span class="token operator">-</span>size<span class="token operator">=</span><span class="token string">"cover"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>image<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>text <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"userinfo-nickname"</span><span class="token operator">&gt;</span><span class="token punctuation">{</span><span class="token punctuation">{</span>userInfo<span class="token punctuation">.</span>nickName<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>text<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>block<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>view<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span>view <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"usermotto"</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>text <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"user-motto"</span><span class="token operator">&gt;</span><span class="token punctuation">{</span><span class="token punctuation">{</span>motto<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>text<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>view<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>view<span class="token operator">&gt;</span></code></pre>
</div>
</div>
<p>和html非常相似但也存在很多不一样的地方，</p>
<ul class="ul-level-0">
<li>标签名字不一样，小程序的标签是<code>view</code>、<code>button</code>、<code>text</code></li>
<li>多了一些 <code>wx:if</code> 这样的属性以及 {{ }} 这样的表达式</li>
<li>提供了类似于vue中的模板语法</li>
</ul>
<h3 id="2itvs">WXSS样式</h3>
<p>WXSS是一套样式语言，用于描述WXML的组件样式，相当于网页开发中的css，WXSS具有CSS的大部分特性，但也做了一些扩充和修改。</p>
<ul class="ul-level-0">
<li>新增了尺寸单位</li>
<li>提供了全局的样式和局部样式。其中app.wxss就是进行全局的样式设置。而page.wxss是进行单个页面的样式设置。</li>
<li><code>WXSS</code> 仅支持部分 <code>CSS</code> 选择器</li>
</ul>
<h3 id="798tf">JS逻辑交互</h3>
<p>一个小程序或者是界面只有静态的样式完全是不能满足人们的需求的，必须有人机交互，这一功能就是通过JS来实现的。小程序的JS文件主要分为三类：app.js是整个小程序的入口；页面的.js文件，是单独页面的入口；普通的.js文件，就是普通的功能模块文件。例如：点击一个按钮的时候，希望界面上显示“Hello”</p>
<div class="rno-markdown-code">
<div class="rno-markdown-code-toolbar">
<div class="rno-markdown-code-toolbar-info">
<div class="rno-markdown-code-toolbar-item"><span class="is-m-hidden">代码语言：</span>javascript</div>
</div>
<div class="rno-markdown-code-toolbar-opt">
<div class="rno-markdown-code-toolbar-opt-icon-btn is-ai-btn">
<div class="chat-ui-ai-logo css-2jdwfl"></div>
<p>AI代码解释</p></div>
</div>
</div>
<div class="developer-code-block">
<div class="rno-markdown-code-opt-float">
<div class="rno-markdown-code-icon-btn qa-r-editor-btn"></div>
</div>
<pre class="prism-token token line-numbers language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">Page</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">clickMe</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setData</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">msg</span><span class="token operator">:</span> <span class="token string">"Hello"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
</div>
</div>
<p>除此之外还可以通过JS调用小程序中的各种<span class="mod-overview__keyword">API</span>，利用这些API可以使用微信提供的丰富功能。比如：微信登录、获取用户信息、微信支付等等。</p>
<p><a href="https://www.dixunblog.cn/1023.html">小程序的组成结构</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/1023.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CoreNext主题1.5.2免授权 &#124; WordPress源码</title>
		<link>https://www.dixunblog.cn/965.html</link>
					<comments>https://www.dixunblog.cn/965.html#respond</comments>
		
		<dc:creator><![CDATA[老大]]></dc:creator>
		<pubDate>Sun, 28 Dec 2025 15:57:26 +0000</pubDate>
				<category><![CDATA[编程代码]]></category>
		<category><![CDATA[CoreNext主题]]></category>
		<category><![CDATA[wordpress模板]]></category>
		<category><![CDATA[主题模板]]></category>
		<guid isPermaLink="false">https://www.dixunblog.cn/?p=965</guid>

					<description><![CDATA[<p>更新日志 v1.5.2 新增 本地头像系统，开启后彻底使用本地头像 优化 SEO 功能 调整 作者边栏动态排序内容 调整 文章引用内容界面 v1.5.1 新增 头像系统 支持用户自行上传图片</p>
<p><a href="https://www.dixunblog.cn/965.html">CoreNext主题1.5.2免授权 | WordPress源码</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>更新日志</p>
<p>v1.5.2</p>
<div>
<div>
<p>新增 本地头像系统，开启后彻底使用本地头像</p>
<p>优化 SEO 功能</p>
<p>调整 作者边栏动态排序内容</p>
<p>调整 文章引用内容界面</p>
</div>
</div>
<p>v1.5.1</p>
<div>
<p>新增 头像系统 支持用户自行上传图片 [主题设置 -&gt; 用户中心 -&gt; 允许用户上传头像]</p>
<p>修复 自动深色模式 logo 用默认</p>
<p>优化 小工具 首篇大图文章高度</p>
<p>调整 获取文章缩略图逻辑</p>
</div>
<h3 id="pk-menu-0">图片：</h3>
<p><img loading="lazy" decoding="async" class="size-full wp-image-970 aligncenter" title="17045_bface2412f4569121835cef0dd47b51a" src="http://cdn.hyclive.cn/dixunblog/2025/12/17045_bface2412f4569121835cef0dd47b51a.png" alt="17045_bface2412f4569121835cef0dd47b51a" width="1630" height="1105" /></p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-971 aligncenter" title="17045_e67ce42c8285181df0ca38834053500d" src="http://cdn.hyclive.cn/dixunblog/2025/12/17045_e67ce42c8285181df0ca38834053500d.png" alt="17045_e67ce42c8285181df0ca38834053500d" width="1629" height="1123" /></p>
<p>&nbsp;</p>
<p>下载地址：<a href="https://www.npspro.cn/wp-content/plugins/third-party-links/go.php/?id=28891&amp;url=aHR0cHM6Ly9zdXlhbjgubGFuem91Yi5jb20vaWp5N1Mxd2l6enhh" target="_blank" rel="nofollow noopener">https://suyan8.lanzoub.com/ijy7S1wizzxa</a></p>
<p><a href="https://www.dixunblog.cn/965.html">CoreNext主题1.5.2免授权 | WordPress源码</a>最先出现在<a href="https://www.dixunblog.cn">帝讯博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dixunblog.cn/965.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
