[{"authors":null,"categories":null,"content":"前言 我的博客使用 Hugo + github-style 主题搭建，源码托管在 GitHub 上。为了同时支持 GitHub Pages 和阿里云服务器访问，我配置了 GitHub Actions 工作流，实现推送代码后自动构建并部署到两个地方。本文记录了整个部署过程中遇到的问题和解决方案。\n整体架构 代码推送到 GitHub main 分支 ↓ GitHub Actions 触发构建 ↓ Hugo 构建静态文件 ↓ ┌────┴────┐ ↓ ↓ GitHub Pages SSH 部署到阿里云 ↓ Docker 容器 (nginx:alpine) ↓ 端口 8000 对外访问 一、GitHub Pages 部署 工作流配置 GitHub Pages 部署相对简单，使用官方 Actions 即可：\n- name: Setup Pages id: pages uses: actions/configure-pages@v4 - name: Build with Hugo run: | hugo --gc --minify --baseURL \u0026#34;${{ steps.pages.outputs.base_url }}/\u0026#34; - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ./public 踩坑：主题目录结构 问题：最初直接将主题文件放在项目根目录，构建时报错：\nError: module \u0026#34;github-style\u0026#34; not found in \u0026#34;themes/github-style\u0026#34; 解决：Hugo 要求主题文件必须放在 themes/ 目录下。将 layouts/、static/ 等文件移动到 themes/github-style/ 目录即可。\n踩坑：静态资源路径 404 问题：部署后图片、CSS 等静态资源返回 404。\n原因：配置文件中使用了绝对路径 /images/avatar.png，在子目录部署时（如 /my-blog/），浏览器会请求 https://qiect.github.io/images/avatar.png，而不是正确的 https://qiect.github.io/my-blog/images/avatar.png。\n解决：将路径改为相对路径：\n# 修改前 favicon = \u0026#34;/images/github-mark.png\u0026#34; # 修改后 favicon = \u0026#34;images/github-mark.png\u0026#34; 踩坑：搜索功能 index.json 404 问题：搜索功能请求 /index.json 返回 404。\n原因：search.js 中硬编码了绝对路径 fetch(\u0026#39;/index.json\u0026#39;)。\n解决：改为相对路径 fetch(\u0026#39;index.json\u0026#39;)。\n二、Gitalk 评论系统集成 踩坑：敏感信息泄露 问题：Gitalk 的 clientID 和 clientSecret 直接写在 config.toml 中，提交到公开仓库不安全。\n解决：使用 GitHub Secrets + 构建时替换的方案：\n配置文件使用占位符：\n[params.gitalk] clientID = \u0026#34;{{ GITALK_CLIENT_ID }}\u0026#34; clientSecret = \u0026#34;{{ GITALK_CLIENT_SECRET }}\u0026#34; 工作流构建前替换：\n- name: Replace Gitalk secrets run: | sed -i \u0026#39;s/{{ GITALK_CLIENT_ID }}/${{ secrets.GITALK_CLIENT_ID }}/g\u0026#39; config.toml sed -i \u0026#39;s/{{ GITALK_CLIENT_SECRET }}/${{ secrets.GITALK_CLIENT_SECRET }}/g\u0026#39; config.toml 在 GitHub 仓库 Settings → Secrets 中添加 GITALK_CLIENT_ID 和 GITALK_CLIENT_SECRET。\n关于私有仓库 最初考虑将仓库改为私有来保护敏感信息，但发现 GitHub 免费账户的私有仓库不支持 GitHub Pages。所以最终选择了 Secrets 方案。\n三、阿里云服务器 Docker 部署 方案选择 选择使用 Docker + nginx 方案，优势：\n环境隔离，不污染宿主机 部署简单，一条命令启动 方便更新和回滚 Dockerfile FROM nginx:alpine COPY . /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [\u0026#34;nginx\u0026#34;, \u0026#34;-g\u0026#34;, \u0026#34;daemon off;\u0026#34;] nginx 配置 server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html index.htm; location / { try_files $uri $uri/ =404; } gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1000; } 踩坑：artifact 下载问题 问题：使用 actions/download-pages-artifact@v3 报错：\nUnable to resolve action actions/download-pages-artifact, repository not found 解决：改用 actions/download-artifact@v4：\n- name: Download artifact uses: actions/download-artifact@v4 with: name: github-pages path: ./artifact 踩坑：artifact.tar 未解压 问题：下载的 artifact 是一个 artifact.tar 压缩包，直接传到服务器无法使用。\n解决：在 Actions 中先解压再传输：\nmkdir -p public cd artifact if [ -f artifact.tar ]; then tar -xf artifact.tar -C ../public/ else cp -r * ../public/ 2\u0026gt;/dev/null || true fi 踩坑：Docker 权限问题 问题：SSH 部署时执行 docker 命令报错：\npermission denied while trying to connect to the docker API 尝试方案 1：将用户添加到 docker 组\nsudo usermod -aG docker github 结果：sudo 需要密码，SSH 非交互模式下无法输入。\n尝试方案 2：配置 sudo 免密码\nsudo visudo # 添加：github ALL=(ALL) NOPASSWD: ALL 结果：✅ 成功！所有 docker 命令前加 sudo 即可。\n最终部署流程 # 1. 停止并删除旧容器 sudo docker stop myblog 2\u0026gt;/dev/null || true sudo docker rm myblog 2\u0026gt;/dev/null || true sudo docker rmi myblog:latest 2\u0026gt;/dev/null || true # 2. 复制文件到服务器 # 3. 构建并运行新容器 cd /tmp/myblog sudo docker build -t myblog:latest . sudo docker run -d \\ --name myblog \\ --restart always \\ -p 8000:80 \\ myblog:latest 四、GitHub Secrets 配置清单 在仓库 Settings → Secrets and variables → Actions 中添加：\nSecret 名称 说明 GITALK_CLIENT_ID Gitalk OAuth App Client ID GITALK_CLIENT_SECRET Gitalk OAuth App Client Secret SERVER_USERNAME 阿里云服务器 SSH 用户名 SERVER_PASSWORD 阿里云服务器 SSH 密码 五、经验总结 路径问题：子目录部署时，所有静态资源路径必须使用相对路径，避免绝对路径导致 404 敏感信息：公开仓库的敏感信息必须使用 GitHub Secrets，构建时动态注入 Artifact 处理：GitHub Actions 的 artifact 可能是压缩包，需要先解压再使用 Docker 权限：非 root 用户执行 Docker 命令需要配置权限，推荐 sudo 免密码方案 容器重启策略：使用 --restart always 确保容器异常退出后自动重启 六、后续优化方向 配置 nginx 反向代理，通过域名 80 端口直接访问 配置 HTTPS 证书（Let’s Encrypt） 添加 CDN 加速 配置服务器监控和告警 部署是一场修行，每个坑都是成长。希望这篇文章能帮到同样在折腾博客部署的你。\n","date":1780642800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"97a29aa7cc2ae79e9ddafefbeae8be94","permalink":"https://chetapp.top/post/github-actions-%E9%83%A8%E7%BD%B2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%9E%E6%88%98/","publishdate":"2026-06-05T15:00:00+08:00","relpermalink":"/post/github-actions-%E9%83%A8%E7%BD%B2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%9E%E6%88%98/","section":"post","summary":"记录从 GitHub Actions 自动构建 Hugo 博客，通过 SSH 部署到阿里云服务器 Docker 容器的完整过程，以及踩过的坑和解决方案","tags":["GitHub Actions","Docker","阿里云","CI/CD","部署"],"title":"GitHub Actions 自动部署博客到阿里云服务器实战","type":"post"},{"authors":null,"categories":null,"content":"Markdown 简介 Markdown 是一种轻量级标记语言，让你可以用简单的语法格式化文本。\n基础语法 标题 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 强调 *斜体* 或 _斜体_ **粗体** 或 __粗体__ ***粗斜体*** 或 ___粗斜体___ 列表 无序列表：\n- 项目 1 - 项目 2 - 子项目 2.1 - 子项目 2.2 有序列表：\n1. 第一项 2. 第二项 3. 第三项 链接和图片 [链接文本](https://example.com) ![图片描述](image-url.png) 代码 行内代码：`code`\n代码块：\n```python def hello(): print(\u0026#34;Hello, World!\u0026#34;) ``` 引用 \u0026gt; 这是一段引用 \u0026gt; 可以多行 表格 | 列1 | 列2 | 列3 | |-----|-----|-----| | A | B | C | | D | E | F | Hugo 扩展 Front Matter --- title: \u0026#34;文章标题\u0026#34; date: 2026-06-05 draft: false tags: [\u0026#34;标签1\u0026#34;, \u0026#34;标签2\u0026#34;] summary: \u0026#34;文章摘要\u0026#34; --- 短代码 {{\u0026lt; details \u0026#34;点击展开\u0026#34; \u0026gt;}} 隐藏内容 {{\u0026lt; /details \u0026gt;}} 写作建议 使用清晰的标题层级 适当使用列表和强调 代码块指定语言以获得语法高亮 添加适当的空行提高可读性 ","date":1780639200,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"5f9960e76c9622300108fbc6e2048e6c","permalink":"https://chetapp.top/post/markdown%E5%86%99%E4%BD%9C%E6%8A%80%E5%B7%A7/","publishdate":"2026-06-05T14:00:00+08:00","relpermalink":"/post/markdown%E5%86%99%E4%BD%9C%E6%8A%80%E5%B7%A7/","section":"post","summary":"掌握 Markdown 语法，提升博客写作效率","tags":["Markdown","写作","技巧"],"title":"Markdown 写作技巧","type":"post"},{"authors":null,"categories":null,"content":"GitHub Actions 简介 GitHub Actions 是 GitHub 提供的持续集成/持续部署（CI/CD）服务，可以自动化你的工作流程。\n工作流配置 创建 .github/workflows/deploy.yml 文件：\nname: Deploy to GitHub Pages on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Hugo uses: peaceiris/actions-hugo@v3 with: hugo-version: \u0026#39;0.125.0\u0026#39; extended: true - name: Build run: hugo --gc --minify - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./public 启用 GitHub Pages 进入仓库 Settings → Pages Source 选择 “GitHub Actions” 保存后工作流会自动运行 部署流程 每次推送到 main 分支时：\nGitHub Actions 自动触发 拉取代码 安装 Hugo 构建静态文件 部署到 GitHub Pages 自定义域名（可选） 在 static 目录创建 CNAME 文件：\nyourdomain.com 然后在域名服务商配置 DNS 指向 GitHub Pages。\n","date":1780635600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"c842f23457cf2e2fad6ab2d4b7bfb288","permalink":"https://chetapp.top/post/github-pages%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2/","publishdate":"2026-06-05T13:00:00+08:00","relpermalink":"/post/github-pages%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2/","section":"post","summary":"使用 GitHub Actions 自动部署 Hugo 博客到 GitHub Pages","tags":["GitHub Actions","CI/CD","部署"],"title":"GitHub Pages 自动部署","type":"post"},{"authors":null,"categories":null,"content":"Gitalk 简介 Gitalk 是一个基于 GitHub Issue 和 Preact 的评论组件，非常适合技术博客使用。\n特点 💬 基于 GitHub Issue，无需额外数据库 🔐 支持 GitHub 登录 🎨 简洁的界面设计 📱 响应式支持 配置步骤 1. 创建 GitHub OAuth App 访问 GitHub Settings → Developer settings → OAuth Apps 点击 “New OAuth App” 填写信息： Application name: 你的应用名称 Homepage URL: 你的博客地址 Authorization callback URL: 同上 2. 获取凭证 注册成功后获取：\nClient ID Client Secret（需要生成） 3. 配置 Hugo 在 config.toml 中添加：\n[params] enableGitalk = true [params.gitalk] clientID = \u0026#34;你的ClientID\u0026#34; clientSecret = \u0026#34;你的ClientSecret\u0026#34; repo = \u0026#34;你的仓库名\u0026#34; owner = \u0026#34;你的GitHub用户名\u0026#34; admin = \u0026#34;管理员用户名\u0026#34; id = \u0026#34;location.pathname\u0026#34; labels = \u0026#34;gitalk\u0026#34; perPage = 15 pagerDirection = \u0026#34;last\u0026#34; createIssueManually = true distractionFreeMode = false 使用说明 当 createIssueManually = false 时，访问文章会自动创建对应的 Issue 当 createIssueManually = true 时，需要管理员手动创建第一个评论 注意事项 确保仓库是公开的 OAuth App 的 callback URL 必须与博客地址一致 Client Secret 不要泄露 ","date":1780632e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"ddf47897ebe9619ffc349f4a8a3a6e53","permalink":"https://chetapp.top/post/gitalk%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E9%9B%86%E6%88%90/","publishdate":"2026-06-05T12:00:00+08:00","relpermalink":"/post/gitalk%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E9%9B%86%E6%88%90/","section":"post","summary":"如何在 Hugo 博客中集成 Gitalk 评论系统","tags":["Gitalk","评论","GitHub"],"title":"Gitalk 评论系统集成","type":"post"},{"authors":null,"categories":null,"content":"Hugo 简介 Hugo 是一个用 Go 语言编写的静态网站生成器，以其构建速度快而闻名。相比其他静态网站生成器，Hugo 有以下优势：\n⚡ 构建速度极快（毫秒级） 🔄 跨平台支持 📦 单一二进制文件，无需复杂依赖 🎨 丰富的主题生态 安装 Hugo macOS brew install hugo Windows choco install hugo-extended Linux snap install hugo 创建新站点 hugo new site myblog cd myblog 添加主题 将主题克隆到 themes 目录：\ngit clone https://github.com/MeiK2333/github-style.git themes/github-style 创建文章 hugo new post/my-first-post.md 本地预览 hugo server -D 访问 http://localhost:1313 即可预览。\n构建生产版本 hugo --gc --minify 生成的静态文件在 public 目录中。\n","date":1780628400,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"d04a4c4c34f4195a53ba46ada66bc88f","permalink":"https://chetapp.top/post/hugo%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E6%8C%87%E5%8D%97/","publishdate":"2026-06-05T11:00:00+08:00","relpermalink":"/post/hugo%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E6%8C%87%E5%8D%97/","section":"post","summary":"详细介绍如何使用 Hugo 和 github-style 主题搭建个人博客","tags":["Hugo","教程","博客"],"title":"Hugo 博客搭建指南","type":"post"},{"authors":null,"categories":null,"content":"如今我们的生活和工作，早就离不开各种各样的软件。手机APP、办公系统、各类行业业务平台，大大小小的软件无处不在，支撑着日常办公、企业运转和各类线上服务。很多人其实都有一个误区：觉得软件只要开发完成、上线能用，就万事大吉，可以一直稳定用下去。\n但现实很直白：不存在永远不用维护的软件，也没有一劳永逸的系统。持续的维护和迭代，才是软件能够长久活下去的根本。不管一款软件刚做出来时多精致、功能多全面，只要停下更新和维护，它就会慢慢跟不上节奏，逐渐老化、出问题，最后彻底被时代淘汰。\n一、时代大环境：技术一直在迭代，软件无法一成不变 互联网和数字化技术，一直都在快速更新迭代，从来不会停下脚步。硬件设备年年更新，电脑系统不停升级，网络标准持续优化，用户的使用需求也在不断变化，软件所处的运行环境，每时每刻都在发生改变。短短十年时间，整个数字行业的底层技术、运行规则、安全标准、兼容模式，全都发生了翻天覆地的变化。\n回头看看十年前，网上涌现过一大批热门工具、小众软件和行业系统，当时很多都是大家的日常刚需。可现在再去看，那些中途停止更新、不再维护迭代的软件，基本全都消失不见了，彻底退出了大家的视野。\n毫不夸张地说：放眼这十年，没有任何一款零维护、零更新的软件，能一直活到今天。这是行业实打实的规律。反观现在依旧坚挺、人人都在用的产品，不管是电脑最基础的Windows、Linux系统，还是各类主流办公、行业软件，从来都没有\u0026#34;一次开发、终身使用\u0026#34;的说法。它们年年升级、月月优化，持续修补问题、更新功能，靠不断维护跟上技术发展的节奏。\n更关键的是，底层系统一直在更新，会不断淘汰老旧接口、换掉过时的运行机制、升级安全规则。如果你的软件原地踏步、停止维护，就会慢慢和新系统\u0026#34;水土不服\u0026#34;，就像老式家电插不上新式智能电路一样，开始出现各种问题：兼容失败、界面错乱、运行卡顿、频繁闪退，到最后核心功能彻底失效，完全没法用。在这样快速更新、优胜劣汰的行业里，软件维护早就不是加分项，而是活下去的基础底线。\n二、维护的本质：像养护房子一样，给软件持续续命 很多人觉得，软件维护就是修Bug、补漏洞，其实远不止如此。维护和迭代，本质上就是给软件续命，让它能一直适应不断变化的使用环境，持续创造价值。关于这个道理，我们用大家都能看懂的\u0026#34;房子\u0026#34;来类比，就特别好理解。\n一套刚装修好、质量不错的房子，如果长期没人住、没人打理、从不检修，过不了几年就会慢慢破败。墙面受潮发霉、墙皮脱落，墙体结构也会慢慢松动；埋在墙里的水管，长期不用不检修，会生锈、堵塞、漏水；小区电梯常年不保养，零件磨损老化，就会频繁出故障、存在安全隐患；家里的电路、五金配件也会氧化老化，埋下漏电、短路的风险。\n房子耐不耐用，从来不完全取决于当初盖得有多好，更取决于日常有没有人打扫、定期有没有人检修翻新。再结实的房子，长期放任不管，最后也会变成没法住的危房。\n软件也是一模一样的道理。一套软件系统，代码架构是它的墙体，接口和数据通道是水电管网，核心运行逻辑是承重结构，各类功能模块和后台服务，就是电梯、电路这些配套设施。软件上线之后，全天24小时不停运行，不断处理数据、接收指令、适配设备，时间久了就会堆积大量冗余数据、产生隐形漏洞、出现兼容问题。再加上外部系统、硬件、网络一直在更新，如果软件一成不变，自然会慢慢跟不上节奏。\n我们坚持长期维护软件，目的其实很明确。第一，及时修复漏洞、清理冗余数据、排查安全风险，解决卡顿、报错、失灵等问题，保证软件稳稳当当运行。第二，紧跟系统、硬件、网络的更新节奏，优化老旧代码、替换废弃接口，避免因为底层环境升级，导致软件直接瘫痪报废。第三，跟着用户需求、行业规则和安全标准的变化，优化旧功能、增加新能力、提升安全性，让软件一直好用、够用、贴合当下的使用需求，不会被市场淘汰。\n三、长期价值：持续维护，才是软件长久存活的核心 坚持对软件做长期维护和迭代，不管是对软件本身、对企业发展，还是对整个数字行业，都有着至关重要的意义，是数字化稳定运行的关键保障。\n对软件本身来说，持续维护就是它的\u0026#34;续命良药\u0026#34;。任何软件刚做出来的时候，都不可能做到完美，也没办法预判未来的技术变化、使用风险和用户新需求。通过一次次维护和更新，软件才能不断补齐短板、提升运行速度、完善功能，从一个基础版本，慢慢变成稳定、安全、好用的成熟产品，大大延长使用寿命，长期发挥价值。而那些停止维护的软件，只会慢慢老化失灵，最后彻底报废。\n对企业和行业来说，软件维护是数字化运营的基石。现在绝大多数企业的业务、管理，全都依赖各类软件系统，软件稳不稳，直接决定业务能不能正常开展、数据安不安全、办公效率高不高。持续维护迭代，能有效避免系统崩溃、数据泄露、功能失效等问题，让企业数字化体系稳定运转，既降低运营成本，又提升工作效率。\n从整个行业来看，Windows、Linux这些底层系统常年更新维护，给所有应用软件提供了稳定、安全的运行环境；而各类应用软件的持续适配和迭代，也反过来完善了数字生态，推动整个行业稳步向前发展。\n从长远角度看，科技进步从来不是靠一次惊天突破，而是靠日复一日的优化和积累。软件的每一次版本更新、漏洞修复、功能升级，都是技术经验的沉淀。无数软件的持续迭代，汇聚成了数字行业前进的动力，推动技术越来越先进、服务越来越完善、生态越来越成熟。\n总而言之，没有不会老化的软件，只有不断迭代的生命力。软件从来不是做完就结束的一次性产品，而是需要长期养护、持续打磨的数字化工具。就像房子需要日常打扫、年年检修才能安心居住，软件也需要长期维护、持续迭代才能稳定运行。\n在技术飞速更新的今天，我们一定要摒弃\u0026#34;重开发、轻维护\u0026#34;的错误认知，坚持长期维护的思维，持续优化、持续适配、持续升级。只有这样，软件才能突破生命周期的限制，在快速变化的时代里长久存活，持续为用户和企业创造价值。\n","date":1780617600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"6b25afdfb7ecb3744361ec1371b0c794","permalink":"https://chetapp.top/post/%E5%86%8D%E5%A5%BD%E7%94%A8%E7%9A%84%E8%BD%AF%E4%BB%B6%E4%B8%8D%E7%BB%B4%E6%8A%A4%E7%BB%88%E4%BC%9A%E8%A2%AB%E6%97%B6%E4%BB%A3%E6%B7%98%E6%B1%B0/","publishdate":"2026-06-05T00:00:00Z","relpermalink":"/post/%E5%86%8D%E5%A5%BD%E7%94%A8%E7%9A%84%E8%BD%AF%E4%BB%B6%E4%B8%8D%E7%BB%B4%E6%8A%A4%E7%BB%88%E4%BC%9A%E8%A2%AB%E6%97%B6%E4%BB%A3%E6%B7%98%E6%B1%B0/","section":"post","summary":"如今我们的生活和工作，早就离不开各种各样的软件。手机APP、办公系统、各类行业业务平台，大大小小的软件无处不在，支撑着日常办公、企业运转和各类线上服务。很多人其实都有一个误区：觉得软件只要开发完成、上线能用，就万事大吉，可以一直稳定用下去。\n但现实很直白：不存在永远不用维护的软件，也没有一劳永逸的系统。持续的维护和迭代，才是软件能够长久活下去的根本。不管一款软件刚做出来时多精致、功能多全面，只要停下更新和维护，它就会慢慢跟不上节奏，逐渐老化、出问题，最后彻底被时代淘汰。\n","tags":["程序人生"],"title":"再好用的软件，不维护，终会被时代淘汰","type":"post"},{"authors":null,"categories":null,"content":"大家好，我是一个菜鸟，最近在搞.Net Core项目，项目里需要往缓存里插一些数据来提高功能的访问速度和效率，一开始我把数据放到了Session里，但是在这里遇到了一个棘手的问题，我存的数据量较大，辣么大有好几兆，可往服务器缓存里写的时候发现数据被截断了，啥意思呢，就是Session觉得他能存多少他就存多少，剩下的数据就丢了，不要了（心里一万个wc，这也太任性了吧），找了好久没有找到解决方案，那么我就灵机一动，想到了另一个菜鸟同事跟我说过Redis，隐隐约约记得是Redis是专门搞缓存的，效率贼高，然后我们就来看看它到底是个什么东西？\n作为菜鸟的我默默的打开了百度，搜索Redis，第一个推荐就是菜鸟教程（果然菜鸟跟菜鸟有着千丝万缕的联系）\n看到了这里发现还是不太明白是啥意思，为啥值是字符串、哈希、列表、集合等类型就被称为数据结构服务器呢？我们接着往下看看\nRedis是开源的，高性能，还可以持久化，把内存里的数据保存在磁盘中，断电死机了开机还能继续用，好nb，优势性能极高，太厉害了，我们赶紧用一用吧。\n大家都知道数据库嘛，一般都是服务端和客户端，那么我们先来看看Redis咋安装的。\n一、Redis的安装 默认端口 6379\n修改配置文件：\n修改地址绑定信息 bind 0.0.0.0 运行所有IP进行访问\nbind 192.168.1.1 仅运行 192.168.1.1 这个IP进行访问\n如果不需要登陆密码，修改如下参数； 设置密码：requirepass foobared（去掉#，并将foobared修改为密码。比如requirepass 123456）\n如果是在腾讯、阿里云或者亚马逊部署的服务器，应在 云平台上的安全组内，开放 6379 端口；\n这些内容都做好了之后，我的Redis客户端还是连不上Redis数据库，咋回事？我就各种查各种问，试了网上的很多方法都不行，不过也有其它的收获~\nwindows客户端无法连接windows服务器的redis\n修改服务器上的redis的配置文件\u0026#34;redis.windows.conf\u0026#34;\n注释掉redis.windows-service.conf 中的bind 127.0.0.1这一行(在前面加#)\n将protected-mode yes 改成 protected-mode no\nrequirepass foobared（去掉#，并将foobared修改为密码。比如requirepass 123456）\nwindows客户端ping 服务器的公网ip\n如果可以ping通，则直接通过redis Desktop Manager 连接远程服务器\n如果不能，关掉服务器的防火墙，找到windows防火墙的高级设置，在入站规则中找到\u0026#34;文件和打印机共享(回显请求 - ICMPv4-In)“设置允许连接并启动，然后在windows客户端测试是否可以ping通 服务器的公网ip，通了就可以远程连接啦\n这个问题花了我很长时间，Redis Desktop Manager 怎么也连接不上，咨询腾讯云的客服人员，他们也只能帮我们看看端口号是否正常开通，\n注意：如果修改完配置文件里的数据，重新启动Redis，直接打开或者启动并不会加载修改后的配置文件，需要手动指定修改后的配置文件\nredis-server.exe redis.windows.conf\n或者\nredis-server.exe redis.conf\n二、安装Redis客户端 推荐Redis Desktop Manager 和 *** ；\n下载windows版的直接安装。\n三、VisualStudio使用Redis组件 ASP.NET Core 使用 Redis 组件，最好的选择当然是 StackExchange.Redis，GitHub 地址：\nhttps://github.com/StackExchange/StackExchange.Redis\n使用很简单，首先安装程序包：\nPM\u0026gt; Install-Package StackExchange.Redis 使用简单示例：\nstatic void Main(string[] args) { //var configurationOptions = new ConfigurationOptions //{ // EndPoints = // { // \u0026#34;10.11.22.1\u0026#34;, \u0026#34;6379\u0026#34;, // \u0026#34;10.11.22.2\u0026#34;, \u0026#34;6379\u0026#34;, // \u0026#34;10.11.22.3\u0026#34;, \u0026#34;6379\u0026#34; // }, // Password = \u0026#34;aqsea3491\u0026#34; //}; ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(\u0026#34;10.11.22.1:6379,10.11.22.1:6379,10.11.22.1:6379,password=123456\u0026#34;); IDatabase db = redis.GetDatabase(); string value = \u0026#34;abcdefg\u0026#34;; db.StringSet(\u0026#34;test_key\u0026#34;, value); value = db.StringGet(\u0026#34;test_key\u0026#34;); Console.WriteLine(value); Console.ReadLine(); } 当然，如果用于生产环境的话，需要再进行封装下，如果我们使用的是 ASP.NET Core 的话，还有一种不用自己封装的选择，那就是\nMicrosoft.Extensions.Caching.Redis，GitHub 地址：\nhttps://github.com/aspnet/Caching/tree/dev/src/Microsoft.Extensions.Caching.Redis\nMicrosoft.Extensions.Caching.Redis 是微软自己封装的 Redis 组件，内部使用的还是 StackExchange.Redis，但在 ASP.NET Core 中使用起来，非常简单。\n首先安装程序包：\nPM\u0026gt; Microsoft.Extensions.Caching.Redis\nStartup.ConfigureServices配置：\npublic void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); // For redis // install-package Microsoft.Extensions.Caching.Redis services.AddDistributedRedisCache(options =\u0026gt; { options.InstanceName = \u0026#34;\u0026#34;; options.Configuration = \u0026#34;10.11.22.1:6379,10.11.22.1:6379,10.11.22.1:6379,password=123456\u0026#34;; }); } 简单使用：\npublic class ValuesController : Controller { private readonly IDistributedCache _distributedCache; public ValuesController(IDistributedCache distributedCache) { _distributedCache = distributedCache; } // GET api/values [HttpGet] public async Task\u0026lt;string\u0026gt; Get() { // redis operate var key = \u0026#34;test_key\u0026#34;; var valueByte = await _distributedCache.GetAsync(key); if (valueByte == null) { await _distributedCache.SetAsync(key, Encoding.UTF8.GetBytes(\u0026#34;world22222\u0026#34;), new DistributedCacheEntryOptions().SetSlidingExpiration(DateTimeOffset.Now.AddSeconds(3000))); valueByte = await _distributedCache.GetAsync(key); } var valueString = Encoding.UTF8.GetString(valueByte); return valueString; } } 测试过程中，发现 Microsoft.Extensions.Caching.Redis 有一个问题，虽然IDistributedCache提供了SetStringAsync方法，但实际插入到 Redis 的值类型，并不是string，而是hash，可以用redis-cli命令进行测试：\n114.55.56.213:6379\u0026gt; get test_key (error) WRONGTYPE Operation against a key holding the wrong kind of value 114.55.56.213:6379\u0026gt; type test_key hash 所以，没办法，只能使用SetAsync，然后读取再由byte转换为string。\n另外，微软封装的Caching，除了 Microsoft.Extensions.Caching.Redis，还有：\nMicrosoft.Extensions.Caching.Abstractions Microsoft.Extensions.Caching.Memory Microsoft.Extensions.Caching.SqlServer（使用 SqlServer 数据库，作为缓存存储） 四、参考资料 https://www.cnblogs.com/xishuai/p/asp-net-core-use-redis.html\nhttps://www.cnblogs.com/dotnet261010/p/12033624.html\n","date":1775402040,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"4a44f0fa9245e893bf70d8ce93618469","permalink":"https://chetapp.top/post/%E8%8F%9C%E9%B8%9F%E6%80%8E%E4%B9%88%E5%9C%A8.netcore%E9%87%8C%E4%BD%BF%E7%94%A8redis%E7%BC%93%E5%AD%98/","publishdate":"2026-04-05T23:14:00+08:00","relpermalink":"/post/%E8%8F%9C%E9%B8%9F%E6%80%8E%E4%B9%88%E5%9C%A8.netcore%E9%87%8C%E4%BD%BF%E7%94%A8redis%E7%BC%93%E5%AD%98/","section":"post","summary":"从零开始在 .Net Core 中集成 Redis 缓存，包括 Redis 安装配置、客户端连接、StackExchange.Redis 和 Microsoft.Extensions.Caching.Redis 的使用","tags":[".NetCore","Redis","缓存","StackExchange.Redis"],"title":"菜鸟怎么在.NetCore里使用Redis缓存","type":"post"},{"authors":null,"categories":null,"content":"Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎，基于RESTful web接口。Elasticsearch是用Java语言开发的，并作为Apache许可条款下的开放源码发布，是一种流行的企业级搜索引擎。Elasticsearch用于云计算中，能够达到实时搜索，稳定，可靠，快速，安装使用方便。官方客户端在Java、.NET（C#）、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示，Elasticsearch是最受欢迎的企业搜索引擎，其次是Apache Solr，也是基于Lucene。\n如何安装ElasticSearch？ 安装JDK\n安装ElasticSearch，下载解压后运行\\bin\\elasticsearch.bat，完成后浏览器输入 http://localhost:9200/ ，看到下面这样就说明安装成功了\n.Net Core如何使用NEST客户端 直接在NuGet里搜索NEST安装就可以用了\n为了遵循.Net Core的IOC原则，我们这里依赖注入NEST，在项目目录中新建Service文件夹，Service下创建EsClientProvider.cs类和IEsClientProvider.cs类，内容如下：\n完成后就可以在Controller里注入了，如下图： 为了更好的使用ElasticSearch，我们可以借助ElasticSearch-Head工具来查询或者新增数据\n如何安装ElasticSearch-Head工具？ 在此我看了网上很多的安装资料，安装步骤比较繁琐或者已经不适应当前的版本了，最新的安装方式如下：\n1.去GitHub上下载最新的ElasticSearch-Head，下载后解压\n2.用cmd命令行的方式cd到elasticsearch-head目录\n3.依次输入以下命令\nnpm install npm run start 这里需要注意的是npm install的时候会安装很多东西，如果网络不稳定，换个网络下载即可，作者碰到的坑就在这里，浪费了很多时间；看到下图的内容就说明head工具安装和运行成功了，祝贺你\n也可以访问 http://localhost:9100/，跟我下面的图内容差不多也说明成功了\n","date":1775400720,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"7fbbd81333035804b6abd07e8f157e44","permalink":"https://chetapp.top/post/dotnetcore%E4%BD%BF%E7%94%A8elasticsearch+nest/","publishdate":"2026-04-05T22:52:00+08:00","relpermalink":"/post/dotnetcore%E4%BD%BF%E7%94%A8elasticsearch+nest/","section":"post","summary":"在 .Net Core 项目中使用 NEST 客户端操作 ElasticSearch，包括 ES 安装、NEST 依赖注入及 ElasticSearch-Head 工具的安装配置","tags":[".NetCore","ElasticSearch","NEST","搜索"],"title":"DotNetCore使用ElasticSearch+NEST","type":"post"},{"authors":null,"categories":null,"content":"写这篇文章的目的是因为现在网上找不到比较好的、比较全的、看完一篇文章就可以搞定这个功能的文章，所以我要在这里借鉴和总结一下实现这个QQ登录遇到的坑和问题。\n一、首先要知道自己要干嘛 现在我们知道要实现QQ登录，那应该怎么去做呢，当然要先去了解QQ提供给我们的一种登录方式，在这里我找到了QQ互联（QQ互联网址），在这里要吐槽一下腾讯这么大个厂，QQ互联这套系统做的是真**，真难用，我在这里遇到了很多坑，从注册填写申请信息到审核等等，差不多用了半个月的时间，给大家讲一下这里遇到的坑，希望大家可以及时的躲开，不要在这上面浪费时间。\n在这里需要审核两次\n1：第一次是对你个人开发者信息的审核，需要填写身份证号，上传手持身份证正面的照片（这里要注意，不能使用手机的前摄像头，会有镜像，拍出来身份证上的信息都是反的，腾讯审核不通过），联系地址一定要精确到户，腾讯要求的，我也不知道为啥，反正我写了一个模糊不给通过。\n2：第二次是对应用的审核，不管你是网站，还是app，都要在这里创建相对应的应用。这里要注意，网站名称一定要写备案时的网站名称，不然审核不给通过。\n创建完成后需要填写网站地址，也就是备案的地址，网站回调地址可以写多个，用分号隔开。主办单位名称，我写到这里的时候就很懵逼，这是个啥意思，随便写了个昵称审核不给通过，后来咨询客服人员才知道这里要写备案时的主办单位，如果是个人肯定是自己的真是姓名，如果是单位就写单位名称。\n这些做完之后，审核需要一天时间，静静的等着就行了，如果审核不通过系统会返回原因，根据原因修改就可以了，不明白的还可以咨询腾讯客服（在网站的下方），在这里我又要吐槽下这个互联网站（开发者信息审核的时候遇到的一个坑，审核不通过在这里是看不到原因的，就很莫名其妙，不知道哪里有问题无从下手，还是我多次咨询客服后解决的，后来才知道在另一个网站也能填写，腾讯开放平台网址）\n审核通过后，我们就可以看QQ互联这个接口的说明文档了（ QQ互联文档地址 ），通过文档我们了解到，QQ登录用了一种协议叫OAuth2.0，这是啥？\n原来如此，明白了，那么我咋用，其实文档里写的很清楚啦，这里我就不多说了，再来看看API文档，看了一遍好像有点不太聪明的样子，不过没关系，不会的东西我们百度呀，经过百度一圈后我大致发现，有两种路线可以实现这个QQ登录：第一种是通过访问API地址，完全的手撸代码，在URL里各种拼接参数，请求URL之类的，给个链接地址看两眼https://blog.csdn.net/weixin_30316097/article/details/101068005；还有一种是通过咱们的Nuget组件（这个就很灵性，我喜欢）,Microsoft.AspNetCore.Authentication.QQ ，这种方式撸的代码就比较少了，而且也比较高级。下面我们就用这种方式来给大家演示如何实现QQ登录。\n二、.NetCore怎么实现QQ登录 1.在Web项目中引用Nuget包 Microsoft.AspNetCore.Authentication.QQ，这里要注意了，组件要求2.2以上的框架。\n2.在项目的配置文件appsetting.json中添加如下的配置 { \u0026#34;Authentication\u0026#34;: { \u0026#34;QQ\u0026#34;: { \u0026#34;AppId\u0026#34;: \u0026#34;你的AppId\u0026#34;, \u0026#34;AppKey\u0026#34;: \u0026#34;你的AppKey\u0026#34; } } } 在这里我发现在他的代码中还需要一块代码，看下图\n3.重点来了，需要在Startup.cs文件中注册认证服务 //注册认证服务 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options =\u0026gt; { //这里填写一些配置信息，默认即可 }) //添加Cookie认证 .AddQQ(qqOptions =\u0026gt; { qqOptions.AppId = Configuration[\u0026#34;Authentication:QQ:AppId\u0026#34;]; //QQ互联申请的AppId qqOptions.AppKey = Configuration[\u0026#34;Authentication:QQ:AppKey\u0026#34;]; //QQ互联申请的AppKey qqOptions.CallbackPath = \u0026#34;/user/callback\u0026#34;; //QQ互联回调地址 //自定义认证声明 qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQOpenId, \u0026#34;openid\u0026#34;); qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQName, \u0026#34;nickname\u0026#34;); qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQFigure, \u0026#34;figureurl_qq_1\u0026#34;); qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQGender, \u0026#34;gender\u0026#34;); }); 这块可以直接拿来用，上面的CallbackPath 一定要写上面QQ互联申请时的地址。完全的复制粘贴后我发现MyClaimTypes.QQOpenId没有引用会报错，然后我开始研究这是个什么东西，知道这个参数是啥，就要明白参数外面的方法是啥意思，我研究了半天ClaimActions.MapJsonKey，这个方法的摘要是这么说的：\n从具有给定键名的json用户数据中选择一个顶级值，并将其作为声明添加。如果找不到密钥或值为空，则不执行操作。\n从而我发现MyClaimTypes.QQOpenId这个的值应该是个字符串，竟然是个字符串的话，那这应该是个静态类，直接引用里面的字段。于是我就创建了MyCalimTypes类。\n就很棒，目前来说不报错了。\n不要忘了使用认证中间件：在Configure方法添加以下代码。\n//使用验证中间件 app.UseAuthentication(); 4.真正关键的时刻来了 下面我们就要开始写登录功能了\n创建一个UserController控制器，在UserController中需要写两个Action，一个用来触发QQ登录，一个用来处理登录成功后的逻辑。例如：\npublic IActionResult Login(string provider = \u0026#34;QQ\u0026#34;, string returnUrl = null) { //第三方登录成功后跳转的地址 var redirectUrl = Url.Action(nameof(ExternalLoginCallbackAsync), new { returnUrl }); var properties = new AuthenticationProperties() { RedirectUri = redirectUrl }; return Challenge(properties, provider); } [Authorize] public async Task ExternalLoginCallbackAsync(string returnUrl = null) { //QQ认证后会默认登录，如果你想自定义登录，可以先注销第三方登录的身份 //await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); string openId = \u0026#34;\u0026#34;, name = \u0026#34;\u0026#34;, figure = \u0026#34;\u0026#34;, gender = \u0026#34;\u0026#34;; //从当前登录用户的身份声明中获取信息（是否有些眼熟，MyClaimTypes就是在Startup里面自定义的那些） foreach (var item in HttpContext.User.Claims) { switch (item.Type) { case MyClaimTypes.QQOpenId: openId = item.Value; break; case MyClaimTypes.QQName: name = item.Value; break; case MyClaimTypes.QQFigure: figure = item.Value; break; case MyClaimTypes.QQGender: gender = item.Value; break; default: break; } } //获取到OpenId后进行登录或者注册（以下作为示范，不要盲目复制粘贴） if (!openId.IsNullOrEmpty()) { //去数据库查询该QQ是否绑定用户 User user = await _dbContext.User.Where(s =\u0026gt; s.QQOpenId == openId).FirstOrDefaultAsync(); if (user != null) { #region 存在则登陆 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Sid, user.Id.ToString())); identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); identity.AddClaim(new Claim(MyClaimTypes.Avator, user.Avatar)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromDays(int.Parse(Configuration[\u0026#34;AppSettings:LoginExpires\u0026#34;]))) // 有效时间 }); user.LastLoginIP = HttpContext.GetUserIP(); user.LastLoginTime = DateTime.Now; //更新登录信息 _dbContext.User.Update(user); await _dbContext.SaveChangesAsync(); #endregion if (returnUrl != null) return Redirect(returnUrl); else return RedirectToAction(\u0026#34;index\u0026#34;, \u0026#34;home\u0026#34;); } else { User userModel = new User(); userModel.QQOpenId = openId; userModel.Name = name; userModel.Avatar = figure; userModel.Gender = gender; userModel.CreateTime = DateTime.Now; //注册 await _dbContext.User.AddAsync(userModel); if (await _dbContext.SaveChangesAsync() \u0026gt; 0) { #region 注册后自动登陆 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); …","date":1775399880,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"53d3521f1f2317ebc3da8bc9b5494ea5","permalink":"https://chetapp.top/post/dotnetcore%E5%AE%9E%E7%8E%B0qq%E7%99%BB%E5%BD%95/","publishdate":"2026-04-05T22:38:00+08:00","relpermalink":"/post/dotnetcore%E5%AE%9E%E7%8E%B0qq%E7%99%BB%E5%BD%95/","section":"post","summary":"在 .Net Core 中使用 Microsoft.AspNetCore.Authentication.QQ 组件实现 QQ 第三方登录，记录从 QQ 互联申请到代码实现的完整过程及踩坑经验","tags":[".NetCore","QQ登录","OAuth2.0","认证"],"title":"DotNetCore实现QQ登录","type":"post"},{"authors":null,"categories":null,"content":"很多技术从业者，很容易陷入一个误区：把自己定义为“码农”，只会被动搬砖、完成需求、交付代码。但真正优秀的工程师，绝不是单纯的代码执行者，而是具备工程思维、沟通能力、职业素养、长期成长思维的复合型人才。\n技术只是立身之本，素养才是拉开人与人差距的核心关键。想要走得更远、走得更稳，就要跳出“码农思维”，以一名优秀工程师的标准要求自己，懂得劳逸结合、持续精进、长期沉淀，打造属于自己的核心竞争力。\n一、工程师的四段完整成长路径 工程师的成长不是一蹴而就的，而是分阶段、有节奏的沉淀过程。不同阶段有不同的核心目标，循序渐进，才能稳步进阶。\n1. 新人积累期（0-2-3年）：沉淀项目经验，养成职业素养 新人阶段最核心的目标，不是追求技术多高深，而是多落地项目、多踩坑复盘、养成良好的职业习惯。这个阶段接触的每一个需求、每一次开发、每一次线上问题排查，都是最宝贵的积累。同时规范自己的开发习惯、沟通方式、工作态度，打好职业基础，为后续成长铺路。\n2. 能力成熟期（3-5年）：固化知识体系，建立职业价值观 度过新人期后，需要从“会干活”转向“懂思考”。不再单纯被动接需求，而是积累系统化的技术知识、工程经验、业务认知，形成属于自己的技术体系和工作方法论。\n更重要的是建立正确的职业观：工作的核心是提升自我价值，薪资和职位，只是价值的附属产物，价格永远在价值附近波动。摒弃打工思维，以成长为核心，主动沉淀、主动精进。\n3. 方向发展期（3-10年）：深耕细分赛道，确定发展方向 技术发展到一定阶段，需要找准自己的深耕方向，拒绝原地踏步、全面平庸。可以根据自身优势和兴趣，选择三大核心赛道：深耕专业技术、转向技术管理、打造技术+业务的全栈综合能力。聚焦一个方向持续深耕，建立不可替代的个人优势。\n4. 事业释放期（长期）：创造核心价值，输出方法论影响他人 高阶工程师的核心价值，不再是自己单打独斗写代码，而是解决复杂问题、创造业务价值、沉淀方法论、赋能团队。通过总结成熟的开发规范、问题排查思路、项目落地方法，带动团队整体成长，从“个人贡献者”成长为“价值输出者”。\n二、优秀工程师的五大核心职业素养 技术决定下限，素养决定上限。真正拉开工程师差距的，从来不是单一的技术能力，而是全方位的职业素养。\n1. 务实的工程能力：靠谱落地，精益求精 工程能力是工程师的立身之本，核心是务实、严谨、落地，拒绝敷衍式开发。\n代码即作品，保持极致洁癖：不写垃圾代码、不写冗余脏代码，严格规范代码格式、命名规则，注释清晰、文档完善。把每一次编码都当作打磨艺术品，保证代码可阅读、可维护、可迭代。\n先设计后开发，拒绝盲目动手：拿到需求不急于敲代码，先梳理业务逻辑、拆分功能模块、理清技术方案、预判潜在风险。思路清晰再落地，大幅减少返工和线上问题。\n语言是工具，适配场景为王：不局限于单一编程语言，不执着于“我只会什么”，而是根据业务场景选择最合适的技术、语言和框架，用最优方案解决问题。\n熟能生巧，持续刻意练习：技术没有捷径，日常多积累、多实操、多复盘，通过持续练习夯实基础，用经验弥补短板，用熟练提升效率。\n2. 高效的沟通能力：高效协作，结果导向 技术是基础，沟通是效率。优秀的工程师，一定是高效的合作者。业内常说的“气功三境界”，完美诠释了工程师的沟通进阶之路。\n炼精化气：熟练多线程一对一沟通：面对不同岗位、不同需求、不同问题，能够精准对接、清晰表达，快速同步信息、对齐认知，高效解决单点问题。\n炼气化神：及时切换沟通方式：发现线上文字沟通效率极低、信息偏差大、问题僵持不下时，主动发起当面沟通，快速碰撞思路、敲定方案、落地结论，不内耗、不拖延。\n炼神反虚：前置沟通，高效开会：需要多人会议协作时，提前私下对齐核心观点、梳理争议点，开会只为敲定结论、同步分工，避免无效讨论，让每一次会议都有结果、有落地。\n同时牢记职场铁律：只沟通不落地、不留痕，等于没沟通。重要工作、问题结论、分工安排，必须通过邮件、纪要等形式留存记录，权责清晰、有据可查。\n（1）专业邮件撰写规范 邮件是职场正式的沟通凭证，代表个人和团队专业度，需严格遵循规范：\n标题简洁精准，像新闻标题一样，一眼看清核心事项；\n换位思考，站在接收者角度梳理内容，明确对方需要知道的信息、需要落地的工作；\n结构清晰、分点罗列，核心结论前置，详细解释后置，方便忙碌的人快速抓重点；\n重点内容加粗、标色、高亮，突出核心信息；\n完整包含五大要素：背景、结论、后续工作、负责人、时间节点；\n秉持对外客户的标准对待每一封邮件，严谨规范、无错漏。\n邮件发送对象规范：收件人为事项直接相关执行人；抄送直属上级、导师、协同同事，同步进度、留存记录。发送原则：确定要发就及时发，确定不用就不发，不确定则优先发送留痕。\n（2）会议纪要标准模板 所有正式沟通、项目会议，必须输出纪要，核心包含四大模块：【沟通讨论的核心问题】、【最终敲定结论】、【后续分工与落地安排】、【个人总结与预期】。确保事事有结论、件件有落地、全程可追溯。\n（3）特殊邮件处理技巧 内容遗漏无需重发邮件，选择全部回复，补充遗漏内容即可；\n需要新增抄送人，全部回复并备注“+XX 知晓”；\n会议邀请优先预定会议室、完善信息，确认无误后再批量群发，避免反复修改。\n3. 纯粹的诚信素养：直面问题，不推诿、不逃避 技术可以成长，能力可以提升，但人品和诚信是工程师的立身底线。优秀的工程师，永远直面问题、主动担当，拒绝推诿甩锅。\n他人反馈Bug：普通人推脱“不是我的问题”，优秀工程师主动承接“我马上排查确认”；\n遇到不会的问题：普通人敷衍“我不会”，优秀工程师坦诚“我目前不熟悉，我核实后马上给你答复”；\n发现隐性问题：普通人视而不见、放任不管，优秀工程师主动排查、快速修复、追溯影响、联动测试；\n业务方案争议：普通人固守边界“我不做”，优秀工程师对比方案优劣，选择更合理、更稳妥的实现方式；\n突发线上问题：普通人计较得失、推脱推诿，优秀工程师优先解决问题，不计较加班、不纠结责任；\n多方排查故障：普通人纠结“不是我的锅”，优秀工程师秉持“先止血、再追责、后复盘”的原则。\n4. 极致的责任心：敬畏线上，成就业务 线上无小事，作为业务系统的守护者，责任心是不可或缺的核心素养。所有工作都要前置思考、主动兜底、敬畏风险。\n项目上线前主动复盘，预判各类异常场景，提前配置监控、制定应急预案；\n坚持代码注释、文档、Wiki沉淀，方便团队迭代交接，不给后人留坑；\n摆正职场认知：上级是引路人和指导者，自己才是工作的第一责任人，主动推进、主动闭环；\n利用闲暇时间主动优化工作流程、代码质量、业务效率，拒绝被动摸鱼；\n对线上环境保持敬畏之心，绝不抱有侥幸心理，杜绝带问题上线；\n一旦出现线上问题，第一时间响应止损，优先保障业务稳定；\n坚守个人职业信用，无论是否在职，都保持专业靠谱的口碑；\n严守保密底线，严格保护用户数据、业务数据、核心技术方案，杜绝外泄、私自分享。\n5. 持续进取：坚持学习、乐于分享、勤于复盘 （1）正确看待 Case Study Case Study 不是批斗大会，而是复盘问题、完善流程、规避重复踩坑的成长机制。遇到线上问题主动组织复盘，多方问题召开专项会议，单方问题邮件同步结论，沉淀经验、优化流程，让团队共同成长。\n（2）常态化学习与输出 技术迭代飞快，停滞不前就是退步。坚持“读、练、讲”三位一体成长：阅读技术文章、书籍、论文、开源项目；将所学落地到工作中、实操个人项目；主动给同事分享、写博客、发技术文章，以输出倒逼输入。\n（3）高频总结与反思 拒绝无效工作，坚持常态化复盘：日报周报沉淀每日工作与思考，每月做成长总结，认真对待每季度述职。在复盘里发现短板、明确方向、迭代自我，实现持续精进。\n三、工程师的成长底线：劳逸结合 优秀的成长不是透支身体、盲目内卷，而是长期主义的劳逸结合。合理规划工作与生活，高效完成工作、坚持持续学习、保持身心健康，拒绝无效加班、拒绝内耗焦虑，以稳定的状态、持续的积累，实现长久的职业成长。\n四、总结 不想做被动搬砖的“码农”，就要以优秀工程师的标准严格要求自己。技术是底气，素养是上限。扎实的工程能力、高效的沟通协作、真诚的职业诚信、极致的责任心、持续进取的学习心态，加上分阶段的稳步成长，才能让自己在技术道路上走得稳、走得远，持续创造价值、成就自我。\n","date":1768608e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"4d0291a0b8118e92f2c261171a57219c","permalink":"https://chetapp.top/post/%E4%BC%98%E7%A7%80%E5%B7%A5%E7%A8%8B%E5%B8%88%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%B4%A0%E5%85%BB%E4%B8%8E%E5%AE%8C%E6%95%B4%E6%88%90%E9%95%BF%E8%B7%AF%E5%BE%84/","publishdate":"2026-01-17T00:00:00Z","relpermalink":"/post/%E4%BC%98%E7%A7%80%E5%B7%A5%E7%A8%8B%E5%B8%88%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%B4%A0%E5%85%BB%E4%B8%8E%E5%AE%8C%E6%95%B4%E6%88%90%E9%95%BF%E8%B7%AF%E5%BE%84/","section":"post","summary":"很多技术从业者，很容易陷入一个误区：把自己定义为“码农”，只会被动搬砖、完成需求、交付代码。但真正优秀的工程师，绝不是单纯的代码执行者，而是具备工程思维、沟通能力、职业素养、长期成长思维的复合型人才。\n技术只是立身之本，素养才是拉开人与人差距的核心关键。想要走得更远、走得更稳，就要跳出“码农思维”，以一名优秀工程师的标准要求自己，懂得劳逸结合、持续精进、长期沉淀，打造属于自己的核心竞争力。\n","tags":["程序人生"],"title":"优秀工程师的基本素养与完整成长路径","type":"post"},{"authors":null,"categories":null,"content":"因为保密性的原因，我需要针对压缩文件加密，也就是在压缩的同时加上密码；然后代码解压带密码的压缩文件，再去处理解压后的文件。\n问题描述 在使用SharpCompress解压带密码的压缩文件时遇到的一系列问题。\n例如：\n1.解压带密码的rar文件，未解压成功，最后改为zip文件；\n2.解压带密码的zip文件，WriteToDirectory时解压过程中会抛异常，后改用WriteToFile；\n解决方案： 在使用SharpCompress解压带密码的rar压缩文件是，报file crc mismatch问题；\nusing (Stream stream = File.OpenRead(tempPath)) { //压缩文件解密 var rarPassword = Configuration[\u0026#34;RARPassword\u0026#34;]; var reader = ReaderFactory.Open(stream, new ReaderOptions() { Password = rarPassword }); while (reader.MoveToNextEntry()) { if (!reader.Entry.IsDirectory) { Console.WriteLine(reader.Entry.Key); Directory.CreateDirectory(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;); reader.WriteEntryToDirectory(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;); } } } 这段代码是把压缩文件的流读取到内存中，再用ReaderFactory去带密解压缩，但在解压缩的时候抛异常，我这里压缩文件里有几十个文件，却只解压了一个文件，所以我想既然可以解压出来一个，说明解密肯定没问题，只不过在解压多个文件的时候抛了异常，内部什么原因就不知道了，只能换个方法。\n于是，我按照下面的代码改了下，一个一个输出，发现还是不行。\nreader.WriteEntryToFile(Path.Combine(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;, reader.Entry.Key)); 没办法，我放弃了RAR方式，改用ZIP。\nusing (var archive = ArchiveFactory.Open(tempPath, new ReaderOptions { Password = rarPassword })) { foreach (var entry in archive.Entries) { if (!entry.IsDirectory) { Console.WriteLine(entry.Key); Directory.CreateDirectory(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;); entry.WriteToDirectory(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;); } } } 用这种方式后，可以把所有的文件解压出来，但每个文件里面都没内容，只是个空文件。\n于是 我又尝试单个文件输出，不直接写整个目录了。\nentry.WriteEntryToFile(Path.Combine(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;, entry.Entry.Key)); 最后可以了。\n最终可行的解压带密码的zip压缩文件代码：\nusing (var archive = ArchiveFactory.Open(tempPath, new ReaderOptions { Password = rarPassword })) { foreach (var entry in archive.Entries) { if (!entry.IsDirectory) { Console.WriteLine(entry.Key); Directory.CreateDirectory(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;); entry.WriteToFile(Path.Combine(@$\u0026#34;{currentDic}/wwwroot/temp/SQL\u0026#34;, entry.Key)); } } } 总结： 最奇葩的是用这种方式解压带密码的rar文件，都是空文件，zip就没问题，知道的兄弟可以留言下。\n","date":1761373440,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1780714839,"objectID":"84ce3abf793951610ecda9de79cd12fb","permalink":"https://chetapp.top/post/sharpcompress%E8%A7%A3%E5%8E%8B%E5%B8%A6%E5%AF%86%E7%A0%81%E7%9A%84rar%E5%8E%8B%E7%BC%A9%E6%96%87%E4%BB%B6%E9%97%AE%E9%A2%98/","publishdate":"2025-10-25T14:24:00+08:00","relpermalink":"/post/sharpcompress%E8%A7%A3%E5%8E%8B%E5%B8%A6%E5%AF%86%E7%A0%81%E7%9A%84rar%E5%8E%8B%E7%BC%A9%E6%96%87%E4%BB%B6%E9%97%AE%E9%A2%98/","section":"post","summary":"使用 SharpCompress 解压带密码的压缩文件时遇到的问题及解决方案，RAR 解压失败后改用 ZIP 格式并逐文件写入的完整过程","tags":[".NetCore","SharpCompress","压缩解压","ZIP"],"title":"SharpCompress解压带密码的RAR压缩文件问题","type":"post"}]