[{"content":"Spring Cloud 以微服务为核心的分布式系统构建标准 这么多小服务，如何管理他们？(服务治理 注册中心 [服务注册 发现 剔除]) nacos 这么多小服务，他们之间如何通讯？ feign 这么多小服务，客户端怎么访问他们？(网关) gateway 这么多小服务，一旦出现问题了，应该如何自处理？ (容错) sentinel 这么多小服务，一旦出现问题了，应该如何排错？(链路追踪) skywalking 这么多小服务，怎么保证同一组事务一的一致性？seata ","date":"2026-07-06T00:00:00Z","permalink":"https://muioo.github.io/p/spring-cloud%E4%B8%80%E4%B8%AA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E6%A1%86%E6%9E%B6/","title":"【Spring Cloud】一个微服务架构框架"},{"content":"问题 C:\\ProgramData\\Microsoft\\Windows\\CapabilityAccessManager目录下占用内存过大\n查看文件占用工具https://wiztree.world/zh/download/ 清理方法 1 2 3 4 5 6 7 8 9 # 停止camsvc服务 net stop camsvc # 强制杀掉承载camsvc的svchost进程，释放文件锁 taskkill /f /im svchost.exe /fi \u0026#34;SERVICES eq camsvc\u0026#34; # 删除超大日志文件 del /f /q \u0026#34;C:\\ProgramData\\Microsoft\\Windows\\CapabilityAccessManager\\CapabilityAccessManager.db-wal\u0026#34; del /f /q \u0026#34;C:\\ProgramData\\Microsoft\\Windows\\CapabilityAccessManager\\CapabilityAccessManager.db-shm\u0026#34; # 重启服务 net start camsvc ","date":"2026-07-02T00:00:00Z","permalink":"https://muioo.github.io/p/win11%E7%A3%81%E7%9B%98%E6%B8%85%E7%90%86%E6%B8%85%E7%90%86c%E7%9B%98/","title":"【Win11磁盘清理】清理c盘"},{"content":"一、命名规范体系 1.1 变量/函数命名 1 2 3 4 5 6 7 8 // 好的示例 uint32_t sensor_raw_value; // 小写下划线，名词结构 void adc_calibration(void); // 动词+名词结构 GPIO_TypeDef* led_gpio_port; // 类型标识明确 // 需避免的反例 int a; // 无意义命名 void func1(); // 信息缺失 1.2 宏定义规范 1 2 3 #define ADC_SAMPLE_TIMES (100) // 全大写+下划线 #define BYTE_TO_BITS(x) ((x)*8) // 带参数的宏用括号包裹 #define IS_VALID_CHANNEL(c) ((c)\u0026gt;0 \u0026amp;\u0026amp; (c)\u0026lt;16) 1.3 类型定义 1 2 3 4 5 6 7 8 9 10 11 typedef enum { LED_STATE_OFF = 0, LED_STATE_ON, LED_STATE_BLINK } led_state_t; // _t类型后缀 typedef struct { GPIO_TypeDef* port; uint16_t pin; uint8_t active_level; } gpio_config_t; // 配置结构体 二、代码结构组织 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 APP/ ├── Inc/ # 全局类型和配置 │ │ ├── config.h # 系统常量（距离阈值、扇区参数、CAN ID） │ │ └── types.h # 全局类型定义 ├── Drivers/ # 第2层：驱动/算法（纯逻辑，不含FreeRTOS任务） │ ├── Inc/ │ │ ├── callback.h\t# 中断服务函数的编写 │ │ └── debug_print.h\t# 串口调试打印 │ └── Src/ │ │ ├── callback.c │ │ └── debug_print.c └── Task/ # 第3层：FreeRTOS任务（使用Drivers，运行在RTOS之上） ├── Inc/ └── Src/ Src/ # STM32CubeMX生成的HAL层代码 Inc/ # STM32CubeMX生成的头文件 Drivers/ # STM32 HAL库（第三方驱动） Docs/ # 项目文档 Middlewares/ # 第三方中间件 ","date":"2026-06-01T00:00:00Z","permalink":"https://muioo.github.io/p/stm32%E5%9F%BA%E4%BA%8Ecubemx%E7%9A%84cmake%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91%E8%A7%84%E5%88%99/","title":"【stm32】基于CubeMx的cmake项目开发规则"},{"content":"开源skills安装（通用可以用于codex） skills的使用 项目级\n1 2 3 4 5 6 # Claude Code 1、在.claude目录中创建skills目录 2、将github下载的skills复制进来 # Codex 1、在.codex目录中创建skills 2、将github下载的skills复制进来 skills 1：superpowers skills 下载地址\nClaude Code 中安装方法\n1 2 # In Claude Code, register the marketplace first: /plugin marketplace add obra/superpowers-marketplace 1 2 # Then install the plugin from this marketplace: /plugin install superpowers@superpowers-marketplace 可能出现的问题（安装通病） 1、安装提示网络有问题，这个一般是没有开代理 或者 没有配置全局代理 skills 2：ui-ux-pro-max-skills 下载地址\n官网\n安装操作\n1 2 /plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill /plugin install ui-ux-pro-max@ui-ux-pro-max-skill skill3： everything claude-code 下载地址\n安装操作\n1 2 3 4 5 6 7 8 # 将此仓库添加为市场 /plugin marketplace add affaan-m/everything-claude-code # 安装插件(要选择项目级还是用户级) /plugin install everything-claude-code@everything-claude-code # 克隆项目 git clone https://github.com/affaan-m/everything-claude-code.git skill4： frontend-design 下载地址\n安装操作\n1 git clone https://github.com/anthropics/skills.git skill5：awesome-codex-skills 下载地址\n1 git clone https://github.com/ComposioHQ/awesome-codex-skills.git skill6：gsap-skills 前端动画样式skills 下载地址\n1 npx skills add https://github.com/greensock/gsap-skills 插件 CodeGraph 下载地址\n1 npx @colbymchenry/codegraph ","date":"2026-05-21T00:00:00Z","permalink":"https://muioo.github.io/p/ai%E7%BC%96%E7%A8%8B%E8%AE%B0%E5%BD%95%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E7%9A%84skills%E4%BB%A5%E5%8F%8A%E4%B8%80%E4%BA%9Bai%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7/","title":"【Ai编程】记录一些常用的skills以及一些ai辅助工具"},{"content":"Maven配置 下载 Maven 进入Maven官网Welcome to Apache Maven – Maven\n进入文件夹，创建 repo 文件夹，用于存放 Maven 本地仓库依赖\n配置环境变量 检查是否配置成功\n配置settings.xml 打开settings.xml\n复制刚才创建的repo目录\n在 settings.xml 的第 56 行\n1 \u0026lt;localRepository\u0026gt;D:\\App\\apache-maven-3.9.14-bin\\apache-maven-3.9.14\\repo\u0026lt;/localRepository\u0026gt; 在 settings.xml 的第 160 行左右，配置阿里云镜像，加速依赖下载\n1 2 3 4 5 6 \u0026lt;mirror\u0026gt; \u0026lt;id\u0026gt;aliyun-maven\u0026lt;/id\u0026gt; \u0026lt;mirrorOf\u0026gt;central\u0026lt;/mirrorOf\u0026gt; \u0026lt;url\u0026gt;https://maven.aliyun.com/repository/public\u0026lt;/url\u0026gt; \u0026lt;blocked\u0026gt;false\u0026lt;/blocked\u0026gt; \u0026lt;/mirror\u0026gt; 在 settings.xml 的 第 192 行，配置jdk，如果项目用到其他的jdk版本，也在这加就可以，我配置了jdk17和jdk21\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 \u0026lt;!-- JDK17 --\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;jdk-17\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;activeByDefault\u0026gt;true\u0026lt;/activeByDefault\u0026gt; \u0026lt;jdk\u0026gt;17\u0026lt;/jdk\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;maven.compiler.source\u0026gt;17\u0026lt;/maven.compiler.source\u0026gt; \u0026lt;maven.compiler.target\u0026gt;17\u0026lt;/maven.compiler.target\u0026gt; \u0026lt;maven.compiler.compilerVersion\u0026gt;17\u0026lt;/maven.compiler.compilerVersion\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;!-- JDK21 --\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;jdk-21\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;jdk\u0026gt;21\u0026lt;/jdk\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;maven.compiler.source\u0026gt;21\u0026lt;/maven.compiler.source\u0026gt; \u0026lt;maven.compiler.target\u0026gt;21\u0026lt;/maven.compiler.target\u0026gt; \u0026lt;maven.compiler.compilerVersion\u0026gt;21\u0026lt;/maven.compiler.compilerVersion\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; 按住 Win + R 运行 CMD ，输入 mvn help:system，如下所示表示配置成功：\nIDEA配置Maven 创建SpringBoot项目 ","date":"2026-04-24T00:00:00Z","permalink":"https://muioo.github.io/p/maven-%E5%AE%89%E8%A3%85maven%E5%AE%89%E8%A3%85%E4%BB%A5%E5%8F%8Aspringboot-maven%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE/","title":"【Maven 安装】Maven安装以及springboot+maven初始化项目"},{"content":"PWM CubeMX配置 计算PWM脉冲周期公式：72MHz ➗ 72 = 1MHz 1M ➗ 100 = 10000Hz，即每秒计数10000次，每100为一个周期。\n配置TIM1为PWM输出，用于控制呼吸灯，预分频器设置为72-1，自动重装载寄存器为100-1，计数到99后从0开始\nPWM模式设置为PWM mode 1 ，比较寄存器的值初始值设置为100，通过控制比较计数器的值可以控制PWM占空比\nPWM mode 1和 PWM mode 2理解如下图，\nHAL库中的PWM 1 2 3 HAL_TIM_PWM_Start(\u0026amp;htim1, TIM_CHANNEL_1);\t//启动 PWM 输出,通道1，tim1 HAL_TIM_PWM_Stop(\u0026amp;htim1, TIM_CHANNEL_1)\t//停止 PWM 输出 __HAL_TIM_SET_COMPARE(\u0026amp;htim1, TIM_CHANNEL_1, new_value);\t//控制占空比，通过设置比较计数器 Encoder CubeMX配置 tim1配置同上PWM配置，tim3采用编码器模式1，选择TI1就是在通道A的边沿上计数如上图所示，每次脉冲计数2次如果没有分频的话。在极性设置类似有效电平机制，设置为下降沿则会将通道一的波形反转，自动重装载寄存器为65535，\n模式 1（TI1）：A 相计数，B 相判向\n只在 TI1（A 相）的边沿计数；TI2（B 相）电平决定方向。\n逻辑：\nA 相上升 / 下降沿 → 计数器 ±1 加还是减，由此时 B 相的电平决定 模式 2（TI2）：B 相计数，A 相判向\n只在 TI2（B 相）的边沿计数；TI1（A 相）电平决定方向。\n逻辑：\nB 相上升 / 下降沿 → 计数器 ±1 加还是减，由此时 A 相的电平决定 极性设置可以这样简单的理解，旋钮左右方向反了→ 随便把 CH1 或 CH2 设为 Inverted 反相\n通过旋钮编码器控制呼吸灯案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //启动编码器 HAL_TIM_Encoder_Start(\u0026amp;htim3, TIM_CHANNEL_ALL); //启动pwm HAL_TIM_PWM_Start(\u0026amp;htim1, TIM_CHANNEL_1); while (1) { OLED_NewFrame(); counter = __HAL_TIM_GET_COUNTER(\u0026amp;htim3); sprintf(message, \u0026#34;counter:%d\u0026#34;,counter); //顺时针扭动 增加 逆时针扭动 减少 //当增加到 100 以后 则为100 if (counter \u0026gt; 60000) { counter = 0; __HAL_TIM_SET_COUNTER(\u0026amp;htim3, counter); } //溢出65535 else if (counter \u0026gt; 100) { counter = 100; __HAL_TIM_SET_COUNTER(\u0026amp;htim3, counter); } //将counter赋值给pwm __HAL_TIM_SET_COMPARE(\u0026amp;htim1, TIM_CHANNEL_1, counter); OLED_PrintString(16, 16, message, \u0026amp;font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } ","date":"2026-04-24T00:00:00Z","permalink":"https://muioo.github.io/p/stm32%E5%AE%9A%E6%97%B6%E5%99%A8%E4%BB%A5%E5%8F%8A%E7%BC%96%E7%A0%81%E5%99%A8/","title":"【stm32】定时器以及编码器"},{"content":"FreeRtos Freertos可以理解为并发，一个cpu快速切换多个任务执行。\n原生串行代码，独占cpu 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int __io_putchar(int ch) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t*)\u0026amp;ch, 1, HAL_MAX_DELAY); return ch; } void UART_TASK(){ uart_time = HAL_GetTick(); printf(\u0026#34;UART OUTPUT,time:%d!!\\r\\n\u0026#34;,uart_time); } void ADC_TASK(){ HAL_Delay(200); adc_time = HAL_GetTick(); printf(\u0026#34;ADC GET DATA,time:%d\\r\\n\u0026#34;,adc_time); HAL_Delay(300); } void KEY_TASK(){ //如果按键被按下 if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_RESET) { //消抖 HAL_Delay(10); if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8); } } } void ALL_TASK(){ UART_TASK(); ADC_TASK(); KEY_TASK(); } FreeRtos Freertos可以理解为并发，一个cpu快速切换多个任务执行。\n原生串行代码，独占cpu 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int __io_putchar(int ch) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t*)\u0026amp;ch, 1, HAL_MAX_DELAY); return ch; } void UART_TASK(){ uart_time = HAL_GetTick(); printf(\u0026#34;UART OUTPUT,time:%d!!\\r\\n\u0026#34;,uart_time); } void ADC_TASK(){ HAL_Delay(200); adc_time = HAL_GetTick(); printf(\u0026#34;ADC GET DATA,time:%d\\r\\n\u0026#34;,adc_time); HAL_Delay(300); } void KEY_TASK(){ //如果按键被按下 if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_RESET) { //消抖 HAL_Delay(10); if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8); } } } void ALL_TASK(){ UART_TASK(); ADC_TASK(); KEY_TASK(); } 6f4d0d7ab7f0be4949764c6637e6e57fcd1ced6b\n","date":"2026-04-21T00:00:00Z","permalink":"https://muioo.github.io/p/stm32freertos%E5%AE%9E%E6%97%B6%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F1/","title":"【stm32】FreeRTOS实时操作系统1"},{"content":"frp下载 链接https://github.com/fatedier/frp/releases/tag/v0.68.0\n服务器配置 服务器上frps.toml\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bindAddr = \u0026#34;0.0.0.0\u0026#34; #服务端监听的 IP 地址，0.0.0.0表示监听所有网络接口 bindPort = 6000 #指定 FRP 服务端的监听端口，客户端将通过这个端口连接到 FRP 服务端 vhostHTTPPort = 28080 #指定虚拟主机 HTTP 服务监听的端口 webServer.addr = \u0026#34;0.0.0.0\u0026#34; #指定 FRP Web UI（管理界面）的绑定地址 webServer.port = 7001 #指定 FRP Web UI 的端口号，可以通过 7500 端口访问管理界面，可以设置成别的 webServer.user = \u0026#34;admin\u0026#34; #Web UI 登录所需的用户名，自由设置 webServer.password = \u0026#34;admin123\u0026#34; #Web UI 登录所需的密码，自由设置 log.to = \u0026#34;/frpslog/frps.log\u0026#34; #指定日志输出的文件路径 log.level = \u0026#34;info\u0026#34; #日志的记录级别，info 表示记录一般的信息 log.maxDays = 3 #设置日志文件保留的天数，超过 3 天的日志文件将被自动删除 auth.method = \u0026#34;token\u0026#34; #指定认证方式，token 表示客户端和服务端通过 Token 进行身份认证 auth.token = \u0026#34;abcdef\u0026#34; #Token 用于客户端和服务端的身份认证，自由设置 allowPorts = [ { start = 6389, end = 6389}, #定义允许客户端使用的端口范围 ] 服务器需要启动服务配置\n1 2 3 4 #进入文件 cd /etc/systemd/system #创建frps.service文件进行配置 vim frps.service 写入内容\n1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description = frp server # 服务名称，可自定义 After = network.target syslog.target Wants = network.target [Service] Type = simple #启动frps的命令，修改为frps的安装路径 ExecStart = /root/wbl/frp/frp_0.68.0_linux_amd64/frps -c /root/wbl/frp/frp_0.68.0_linux_amd64/frps.toml [Install] WantedBy = multi-user.target 注意：服务器需要打开7001、6389、6000三个端口 控制端配置（win） 打开frpc.toml文件\n1 2 3 4 5 6 7 8 9 10 11 12 13 serverAddr = \u0026#34;服务器ip\u0026#34; serverPort = 6000 #与服务器上的端口一致 auth.token = \u0026#34;abcdef\u0026#34; # 与服务器上的token一致 [transport.tls] enable = true [[proxies]] name = \u0026#34;windows-rdp\u0026#34; type = \u0026#34;tcp\u0026#34; localIP = \u0026#34;127.0.0.1\u0026#34; localPort = 3389 remotePort = 6389 在安装路径中打开cmd，执行frpc -c frpc.toml，最好将这个服务设置为开机自启，可以使用nssm\n","date":"2026-04-09T00:00:00Z","permalink":"https://muioo.github.io/p/frpfrp%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8Fp2p%E8%BF%9C%E7%A8%8B%E6%A1%8C%E9%9D%A2%E6%8E%A7%E5%88%B6rdp/","title":"【frp】FRP内网穿透P2P远程桌面控制（RDP）"},{"content":"激活方法 激活方式如下：\n输入cmd 以管理员启动\n1 2 3 slmgr -ipk W269N-WFGWX-YVC9B-4J6C9-T83GX slmgr -skms kms.0t.net.cn slmgr -ato ","date":"2026-04-09T00:00:00Z","permalink":"https://muioo.github.io/p/win11%E4%B8%93%E4%B8%9A%E7%89%88%E6%BF%80%E6%B4%BBwin11%E4%B8%93%E4%B8%9A%E7%89%88%E6%BF%80%E6%B4%BB/","title":"【Win11专业版激活】win11专业版激活"},{"content":"STM32 中断系统 1嵌套向量中断控制器 中断可以由硬件或者软件触发。在ARM处理器中，把能够打断当前代码执行流程的事件分为异常（exception）和中断（interrupt）两类。二者的区别在于：中断是由内核外部产生的，一般由硬件触发，比如定时器中断和外部中断等。而异常通常是内核自身产生的，大多由软件触发，比如除法出错异常，预取指失败等。\n在基于Cortex-M内核设计的ARM处理器中提供了一个专用的硬件模块：嵌套向量中断控制器 （nested vectored interrupt controller，NVIC）用来管理全部的异常和中断。通过对NVIC的编程，可以实现中断的使能、中断优先级的设置以及中断触发方式的选择等功能。\nNVIC利用中断通道来管理各类中断，中断源通过中断通道向内核发出中断申请，这些中断通道已经固定分配给内核异常和片内外设。每一个中断通道对应一个中断服务程序，按照中断优先级的顺序组成了STM32微控制器 的中断向量表。不同型号的STM32微控制器支持的中断通道各不相同\nSTM32中断优先级设置 中断源通过中断通道向内核发出中断申请，设置中断源的优先级实际上是设置中断通道的优先级。中断通道的优先级通过NVIC中的中断优先级寄存器NVIC_IP进行设置，该寄存器是8位，理论上可以配置256个中断优先级。STM32微控制器只使用了其中的高4位，并分成了两个优先级：抢占优先级（preempition priority）和子优先级（subpriority）。\n抢占优先级：无关中断产生的先后，只比较优先级的高低。\n子优先级：就是响应优先级，响应优先级主要给出了一种响应的优先队列。\n具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应，即中断嵌套，或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。通俗的理解就是，低抢占优先级的中断处理正在执行，来了一个高抢占式优先级的中断，即去执行高抢占式优先级的中断，执行完毕后，再去执行低抢占优先级的中断。\n当两个中断源的抢占式优先级相同时，这两个中断将没有嵌套关系，当一个中断到来后，如果正在处理另一个中断，这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达，则中断控制器根据他们的响应优先级高低来决定先处理哪一个；如果他们的抢占式优先级和响应优先级都相等，则根据他们在中断表中的排位顺序决定先处理哪一个。\n具体的分组情况如下：\n第0组：所有4位用于指定子优先级。\n第1组：最高1位用于指定抢占优先级，后面3位用于指定子优先级。\n第2组：最高2位用于指定抢占优先级，后面2位用于指定子优先级。\n第3组：最高3位用于指定抢占优先级，后面1位用于指定子优先级。\n第4组：所有4位于指定抢占优先级。\n不同分组情况下，抢占优先级和子优先级的等级划分如下表所示。\n优先级分组 抢占优先级 子优先级 第0组：NVIC_PriorityGroup_0 无 4位/16级（0~15） 第1组：NVIC_PriorityGroup_1 1位/2级（0~1） 3位18级（0~7） 第2组：NVIC_PriorityGroup_2 2位/4级（0~3） 2位/4级（0~3） 第3组：NVIC_PriorityGroup_3 3位/8级（0~7） 1位12级（0~1） 第4组：NVIC_PriorityGroup_4 4位/16级（0~15） 无 在判断一个中断的优先级时，需要综合考虑这两个优先级。当多个中断同时提出中断申请时，抢占优先级高的中断会优先得到执行。如果抢占优先级相同，则比较子优先级；子优先级高的中断会优先得到执行。如果抢占优先级和子优先级都相同的话，就根据各中断在中断向量表中的位置来确定，中断向量表中靠前的中断优先得到执行。\n在HAL库的初始化过程中，HAL库初始化函数HAL_Init()将优先级分组设置为第4组，即只有0~15共16级抢占式优先级，没有子优先级。编号越小的优先级越高：0号为最高，15号为最低。\nstm32中断代码案例 根据上方的优先级可以设置抢占优先级或者响应优先级，要注意key引脚的接法，这里我的引按键引脚是PC5\n情况 A：你按键是 一端接 PC5，一端接 GND 这是开发板最常见接法\n松开：PC5 = 1（上拉）\n按下：PC5 = 0（到地）\n这种接法必须：\n上拉 + 下降沿\n情况 B：如果按键是 一端接 PC5，一端接 3.3V\n松开：PC5 = 0（下拉）\n按下：PC5 = 1（到电源）\n这种需要：\n下拉 + 上升沿\n使用按键触发中断，当按下key0时电平翻转，点亮红灯\n在main.c中重写HAL_GPIO_EXTI_Callback()函数\n定时器与中断闪灯 SysTick_Handler()函数，SysTick 是 Cortex-M 内核自带的定时器（系统滴答定时器）。\n默认配置：每 1ms 溢出一次\n溢出后自动进入中断\n进入中断就会执行：\n1 2 3 4 void SysTick_Handler(void) { // 这里的代码每 1ms 自动跑一次！ } ","date":"2026-04-07T00:00:00Z","permalink":"https://muioo.github.io/p/stm32stm32%E4%B8%ADhal%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%AE%9A%E6%97%B6%E5%99%A8%E4%BB%A5%E5%8F%8A%E4%B8%AD%E6%96%AD%E7%9A%84%E5%AE%9E%E7%8E%B0/","title":"【stm32】STM32中HAL库系统定时器以及中断的实现"},{"content":"nginx详细解读 nginx在项目部署中的作用 生产环境中请求接口不走vite.config.ts中的代理，而是nginx.conf ","date":"2026-04-02T00:00:00Z","permalink":"https://muioo.github.io/p/nginxnginx%E7%9A%84%E7%90%86%E8%A7%A3%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8/","title":"【nginx】nginx的理解以及使用"},{"content":"Node.js安装流程 下载安装包 ：\n访问 Node.js 官方下载页面 测试安装\n1 2 node -v // 检查 Node.js 版本 npm -v // 检查 npm 版本 环境配置 打开安装目录，新建两个文件夹 node_global 和 node_cache\n配置 npm 路径\n以管理员身份打开CMD\n替换为刚才创建的文件路径\n1 npm config set prefix \u0026#34;E:\\App\\nodejs\\node_global\u0026#34; 1 npm config set cache \u0026#34;E:\\App\\nodejs\\node_cache\u0026#34; 使用一下命令验证是否成功 1 2 npm config get prefix npm config get cache 配置环境变量\n变量名：NODE_PATH 变量值：E:\\App\\nodejs\\node_global\\node_modules （复制刚刚创建的node global路径并在后面添加\\node modules） 编辑用户变量 Path：在 用户变量 区域，选择 Path 变量，点击 编辑 将默认的 C:\\Users\\你的用户名\\AppData\\Roaming\\npm 路径修改为 node_global 文件夹的路径（如 E:\\App\\nodejs\\node_global），然后点击确定。 更新系统变量 Path：在 系统变量 区域，选择 Path，点击 **编辑 **点击 新建，输入 %NODE_PATH% 测试 如果输出E:\\App\\nodejs\\node_global\\node_modules 表示成功 1 echo %NODE_PATH% 测试 输入下面命令进行安装 安装成功后，node_global 文件夹下会生成 node_modules 目录\n1 npm install express -g 配置镜像 1 npm config set registry https://registry.npmmirror.com ","date":"2026-03-12T00:00:00Z","permalink":"https://muioo.github.io/p/node%E5%AE%89%E8%A3%85npm-cnpm-%E7%AD%89%E5%91%BD%E4%BB%A4%E7%9A%84%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E5%AE%89%E8%A3%85/","title":"【Node安装】npm cnpm 等命令的安装使用安装"},{"content":"服务器部署 阿里云服务器docker配置镜像加速 创建/etc/docker目录 1 mkdir -p /etc/docker 写入镜像地址 1 2 3 4 5 6 7 tee /etc/docker/daemon.json \u0026lt;\u0026lt;-\u0026#39;EOF\u0026#39; { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://docker.m.daocloud.io\u0026#34; ] } EOF 重启docker 1 systemctl daemon-reload \u0026amp;\u0026amp; systemctl restart docker 阿里云服务器为docker配置本地代理 创建/etc/systemd/system/docker.service.d文件 1 mkdir -p /etc/systemd/system/docker.service.d 查看文件内容并且写入 1 2 3 4 5 cat \u0026gt; /etc/systemd/system/docker.service.d/proxy.conf \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; [Service] Environment=\u0026#34;HTTP_PROXY=http://127.0.0.1:7890\u0026#34; Environment=\u0026#34;HTTPS_PROXY=http://127.0.0.1:7890\u0026#34; EOF 重启docker 1 systemctl daemon-reload \u0026amp;\u0026amp; systemctl restart docker 验证代理是否生效 1 systemctl show --property=Environment docker # 出现这个表示成功Environment=HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 Docker部署网络图 1 2 3 4 5 1、Dockerfile是构建Docker镜像的基础,Docker通过读取Dockerfile中的指令，执行相应的操作（如添加文件、安装软件包、配置环境变量等），最终生成一个镜像 2、Docker镜像是Docker容器的基础，docker build时会产生一个Docker镜像，当运行 Docker镜像时会真正开始提供服务 3、Docker容器，依据镜像运行（docker run）容器提供服务 Docker使用 数据库使用 1 2 # 进入数据库（mysql为docker创建的mysql名称，建议不要像我这样同名） docker exec -it mysql mysql -uroot -proot123456 docker中的数据库可以看到与宿主机的mysql是隔离的，当前没有映射宿主机端口。\nDockerfile文件使用 Dockerfile的基本结构包括：\n命令 含义 基础镜像（FROM） 指定构建新镜像所使用的基础镜像，在Dockerfile中第一条指令必须是FROM指令 设置工作目录（WORKDIR） 指定后续指令的工作目录。 复制文件（COPY/ADD) 将文件或目录复制到镜像中。ADD指令还可以自动解压压缩文件，但出于安全考虑，一般推荐使用COPY指令。 安装软件包（RUN） 在镜像中运行命令，如安装软件包。RUN指令常用于安装依赖、编译程序等。 配置环境变量（ENV） 设置环境变量，供镜像中运行的程序使用。 暴露端口（EXPOSE） 声明镜像中运行的应用将使用容器的哪个端口。不过，这并不会让端口自动在宿主机上监听，而是需要在运行容器时通过-p或-P参数来指定 容器启动时要运行的命令（CMD） Dockerfile中可以包含多个CMD指令，但只有最后一个生效。CMD指令可以被docker run命令行中的参数覆盖 配置容器启动时运行的可执行文件(ENTRYPOINT) 与CMD不同，CMD的指令会被当作参数传递给ENTRYPOINT 声明容器运行时监听的端口(EXPOSE) 只是声明，并不会自动使端口对外提供服务 LABEL 为镜像添加元数据 ENV 设置环境变量 VOLUME 创建一个可以从本地主机或其他容器挂载的挂载点，一般用来存放数据库和需要保持的数据等 USER 指定运行容器时的用户名或UID HEALTHCHECK 用于指定一个检查容器健康状态的命令 SHELL 允许覆盖用于命令的shell形式 docker-compose.yml文件用于自动化部署\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 services: backend: build: context: . dockerfile: backend/Dockerfile container_name: knowledge-backend restart: unless-stopped env_file: - ./backend/.env environment: DB_HOST: mysql DB_PORT: 3306 DB_USER: knowledge_user DB_PASSWORD: Knowledge@123 DB_NAME: knowledge_system volumes: - ./backend/uploads:/app/backend/uploads - ./logs:/app/logs networks: - knowledge-net healthcheck: test: [\u0026#34;CMD\u0026#34;, \u0026#34;python\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;import urllib.request; urllib.request.urlopen(\u0026#39;http://localhost:8022/health\u0026#39;)\u0026#34;] interval: 30s timeout: 10s retries: 5 start_period: 60s frontend: build: context: ./frontend dockerfile: Dockerfile container_name: knowledge-frontend restart: unless-stopped ports: # 服务器端口5173 映射 到docker容器内端口80，只暴露前端端口 后端8022不暴露 - \u0026#34;5173:80\u0026#34; volumes: # 挂载本地构建好的 dist 目录（本地构建后直接生效，无需重新构建镜像） - ./frontend/dist:/usr/share/nginx/html:ro depends_on: - backend networks: - knowledge-net networks: knowledge-net: driver: bridge external: false backend/dockfile文件\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 FROM python:3.11-slim # 工作目录设为 /app，backend 代码放在 /app/backend/ 下，保持包路径 backend.main:app # 如果还需要部署其他的项目【新项目独立工作目录】→ 叫 /app2 或 /newapp 都行，不和旧项目冲突 WORKDIR /app # 安装系统依赖，一般都是安装依赖 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y --no-install-recommends \\ gcc \\ default-libmysqlclient-dev \\ pkg-config \\ \u0026amp;\u0026amp; rm -rf /var/lib/apt/lists/* # 复制依赖文件并安装（build context 是项目根目录） COPY backend/requirements-docker.txt . RUN pip install --no-cache-dir -r requirements-docker.txt # 复制 aerich 配置和迁移文件（需要在 /app 根目录，aerich 从这里读取） COPY pyproject.toml ./pyproject.toml COPY backend/migrations ./migrations # 复制 backend 代码到 /app/backend/ COPY backend ./backend # 修复 entrypoint.sh 换行符（CRLF -\u0026gt; LF）并设置可执行权限 RUN sed -i \u0026#39;s/\\r$//\u0026#39; ./backend/entrypoint.sh \u0026amp;\u0026amp; \\ chmod +x ./backend/entrypoint.sh # 新项目记得不要和旧项目的端口冲突 EXPOSE 8022 ENTRYPOINT [\u0026#34;./backend/entrypoint.sh\u0026#34;] frontend/dockerfile文件\n1 2 3 4 5 6 7 # 生产阶段 - 直接使用本地构建好的 dist 目录 FROM nginx:alpine # 只复制 nginx.conf，dist 目录通过 docker-compose volumes 挂载 COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 实操部署流程 代码仓库https://github.com/muioo/knowledge-system.git 数据库迁移脚本. /backend/entrypoint.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 #!/bin/sh # 不使用 set -e，以便显示错误信息 echo \u0026#34;=== Waiting for MySQL to be ready ===\u0026#34; MAX_RETRIES=30 RETRY=0 while true; do if python -c \u0026#34; import asyncio, sys from tortoise import Tortoise from backend.settings.config import TORTOISE_ORM async def check(): try: await Tortoise.init(config=TORTOISE_ORM) await Tortoise.close_connections() print(\u0026#39;OK\u0026#39;) except Exception as e: print(f\u0026#39;ERROR: {e}\u0026#39;, file=sys.stderr) sys.exit(1) asyncio.run(check()) \u0026#34; 2\u0026gt;\u0026amp;1; then break fi RETRY=$((RETRY + 1)) if [ $RETRY -ge $MAX_RETRIES ]; then echo \u0026#34;MySQL not ready after ${MAX_RETRIES} retries, exiting.\u0026#34; exit 1 fi echo \u0026#34; MySQL not ready yet, retrying in 2s... ($RETRY/$MAX_RETRIES)\u0026#34; sleep 2 done echo \u0026#34;MySQL is ready.\u0026#34; echo \u0026#34;=== Running aerich migrations ===\u0026#34; # 使用 aerich CLI 但先读取迁移文件，分割后逐条执行 python \u0026lt;\u0026lt; \u0026#39;PYTHON_SCRIPT\u0026#39; import asyncio import sys import traceback import os import re from pathlib import Path async def init_db_with_tortoise(): \u0026#34;\u0026#34;\u0026#34;使用 TortoiseORM 直接初始化数据库表（无 migrations 时）\u0026#34;\u0026#34;\u0026#34; from tortoise import Tortoise from backend.settings.config import TORTOISE_ORM print(\u0026#34;No migrations directory found, using TortoiseORM to initialize database...\u0026#34;) await Tortoise.init(config=TORTOISE_ORM) await Tortoise.generate_schemas() print(f\u0026#34;Database tables created successfully!\u0026#34;) await Tortoise.close_connections() async def migrate(): try: from aerich import Command from aerich.models import Aerich from backend.settings.config import TORTOISE_ORM from tortoise import Tortoise, connections print(\u0026#34;Initializing aerich...\u0026#34;) # 初始化 Tortoise 和 aerich await Tortoise.init(config=TORTOISE_ORM) command = Command(tortoise_config=TORTOISE_ORM, location=\u0026#39;migrations\u0026#39;) await command.init() print(\u0026#34;Checking for pending migrations...\u0026#34;) # 检查是否有待执行的迁移 migrations_path = Path(\u0026#39;migrations/models\u0026#39;) if not migrations_path.exists(): print(\u0026#39;No migrations directory found\u0026#39;) # 使用 TortoiseORM 直接初始化数据库表 await init_db_with_tortoise() return # 获取所有迁移文件 migration_files = sorted(migrations_path.glob(\u0026#39;*.py\u0026#39;)) if not migration_files: print(\u0026#39;No migration files found\u0026#39;) # 使用 TortoiseORM 直接初始化数据库表 await init_db_with_tortoise() return # 获取已应用的迁移 applied_versions = [] try: applied = await Aerich.all().values(\u0026#39;version\u0026#39;) applied_versions = [a[\u0026#39;version\u0026#39;] for a in applied] print(f\u0026#34;Already applied migrations: {applied_versions}\u0026#34;) except Exception as e: print(f\u0026#34;No aerich table yet (first run): {e}\u0026#34;) pass # 首次运行，aerich 表还不存在 # 找出待执行的迁移 pending = [] for f in migration_files: if f.name.startswith(\u0026#39;0_\u0026#39;): # 初始迁移特殊处理 if not applied_versions: pending.append(f) else: # 检查完整文件名是否在已应用列表中 if f.name not in applied_versions: pending.append(f) if not pending: print(\u0026#39;No new migrations to run\u0026#39;) await Tortoise.close_connections() return print(f\u0026#34;Found {len(pending)} pending migrations: {[f.name for f in pending]}\u0026#34;) # 逐个执行迁移 for i, migration_file in enumerate(pending, 1): print(f\u0026#34; [{i}/{len(pending)}] Applying {migration_file.name}...\u0026#34;) # 动态导入迁移模块 module_name = f\u0026#34;migrations.models.{migration_file.stem}\u0026#34; module = __import__(module_name, fromlist=[\u0026#39;upgrade\u0026#39;]) # 获取升级 SQL conn = connections.get(\u0026#39;default\u0026#39;) upgrade_sql = await module.upgrade(conn) # 分割 SQL 语句 # 简单方法：按分号分割，然后过滤空语句 statements = [] # 移除多行注释 /* ... */ import re upgrade_sql = re.sub(r\u0026#39;/\\*.*?\\*/\u0026#39;, \u0026#39;\u0026#39;, upgrade_sql, flags=re.DOTALL) # 移除单行注释 -- upgrade_sql = re.sub(r\u0026#39;--.*?$\u0026#39;, \u0026#39;\u0026#39;, upgrade_sql, flags=re.MULTILINE) # 按分号分割并清理 raw_statements = upgrade_sql.split(\u0026#39;;\u0026#39;) for stmt in raw_statements: # 移除前后空白和空行 cleaned = \u0026#39;\\n\u0026#39;.join(line.strip() for line in stmt.split(\u0026#39;\\n\u0026#39;) if line.strip()) if cleaned: statements.append(cleaned) print(f\u0026#34; Found {len(statements)} SQL statements to execute\u0026#34;) # 逐条执行 SQL for j, stmt in enumerate(statements, 1): try: # 清理语句 stmt_clean = stmt.strip().rstrip(\u0026#39;;\u0026#39;) if not stmt_clean: continue await conn.execute_script(stmt_clean) print(f\u0026#34; [{j}/{len(statements)}] Executed successfully\u0026#34;) except Exception as e: error_str = str(e) # 跳过已存在的错误 if \u0026#39;already exists\u0026#39; in error_str or \u0026#39;Duplicate column\u0026#39; in error_str or \u0026#39;Duplicate key\u0026#39; in error_str: print(f\u0026#34; [{j}/{len(statements)}] Skipped (already exists)\u0026#34;) continue print(f\u0026#34; [{j}/{len(statements)}] ERROR: {e}\u0026#34;) print(f\u0026#34; SQL: {stmt_clean[:200]}...\u0026#34;) # 不抛出异常，继续执行下一条语句 # 记录迁移 try: # 使用完整文件名作为版本 version = migration_file.name await Aerich.create( version=version, app=\u0026#39;models\u0026#39;, content=upgrade_sql ) print(f\u0026#34; [{i}/{len(pending)}] Recorded migration: {migration_file.name}\u0026#34;) except Exception as e: if \u0026#39;Duplicate entry\u0026#39; in str(e): print(f\u0026#34; [{i}/{len(pending)}] Migration already recorded\u0026#34;) else: raise print(\u0026#39;All migrations completed successfully\u0026#39;) except Exception as e: print(f\u0026#34;Migration error: {e}\u0026#34;) traceback.print_exc() sys.exit(1) asyncio.run(migrate()) PYTHON_SCRIPT echo \u0026#34;=== Migrations complete, starting server ===\u0026#34; exec uvicorn backend.main:app --host 0.0.0.0 --port 8022 数据库连接脚本./init-db.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #!/bin/bash # 在服务器上运行此脚本初始化数据库 echo \u0026#34;=== 初始化 MySQL 数据库 ===\u0026#34; # 连接到 MySQL 容器并创建数据库和用户(docker创建的mysql容器名) docker exec -i mysql mysql -uroot -proot123456 \u0026lt;\u0026lt;EOF -- 创建数据库 CREATE DATABASE IF NOT EXISTS knowledge_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建用户（如果不存在） CREATE USER IF NOT EXISTS \u0026#39;knowledge_user\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;Knowledge@123\u0026#39;; -- 授予权限 GRANT ALL PRIVILEGES ON knowledge_system.* TO \u0026#39;knowledge_user\u0026#39;@\u0026#39;%\u0026#39;; FLUSH PRIVILEGES; -- 显示结果 SHOW DATABASES; SELECT User, Host FROM mysql.user WHERE User=\u0026#39;knowledge_user\u0026#39;; EOF echo \u0026#34;=== 数据库初始化完成 ===\u0026#34; 拉取代码 1 git clone -b production https://github.com/muioo/knowledge-system.git 配置.env文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 应用配置 SECRET_KEY=... DEBUG=false # 数据库配置（连接已有的 MySQL 容器） DB_HOST=mysql DB_PORT=3306 DB_USER=knowledge_user DB_PASSWORD=Knowledge@123 DB_NAME=knowledge_system # CORS 配置（填写你的服务器 IP 和前端端口） CORS_ORIGINS=[\u0026#34;ip:port\u0026#34;] # 火山引擎 AI 配置 ARK_API_KEY= # 前端端口（docker-compose 使用） FRONTEND_PORT=5173 构建项目 1 docker compose -d build 后续更新代码 1 2 3 4 git pull origin production docker compose up -d --build backend # 构建镜像 启动容器 docker compose up -d frontend # 启动容器 docker-compose.yml中的frontend docker compose up -d backend ","date":"2026-03-01T00:00:00Z","image":"https://muioo.github.io/p/docker%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2docker%E9%85%8D%E7%BD%AE-%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE/page_hu_443b9172da723ece.png","permalink":"https://muioo.github.io/p/docker%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2docker%E9%85%8D%E7%BD%AE-%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE/","title":"【docker项目部署】服务器部署Docker配置+部署项目"},{"content":"前端设计规则2（vue+ts） 您是一位精通TypeScript、Node.js、Vite、Vue.js、Vue Router、Pinia、VueUse、Headless UI、Element Plus和Tailwind CSS的专家，对这些技术中的最佳实践和性能优化技巧有深刻理解。 代码风格与结构 编写简洁、可维护且技术准确的TypeScript代码，并提供相关示例。\n如果文件已存在，则在已存在的文件上修改代码\n使用函数式和声明式编程模式；避免使用类。\n目录结构遵循如下的结构\n1 2 3 4 5 6 7 8 9 - src/ - api/ # 统一管理接口请求 如 user.ts 封装用户 CRUD 接口 - components/ # 公共组件库，存放可复用的 UI 组件 - router/ # 定义项目的路由规则 - views/ # 页面级组件，对应路由的 “页面”,如user页面，目录结构为 views/user/index.vue - static/ # 存放图片、字体、全局样式文件，不需要编译的静态文件 - store/ # 用 Pinia 管理全局状态 - utils/ # 工具函数 - types/ # 存放接口请求 / 响应、组件 props 等的类型声明文件 命名约定 目录使用小写字母和短横线（例如：components/auth-wizard）。 倾向于使用命名导出函数。 TypeScript使用 所有代码均使用TypeScript；优先选择接口而非类型，因其可扩展性和合并能力。 避免使用枚举；改用映射以获得更好的类型安全性和灵活性。 使用带有TypeScript接口的函数式组件。 语法与格式化 对于纯函数，使用function关键字以利用提升和清晰性。 始终使用Vue Composition API的script setup风格。 使用VueUse处理常见的组合式函数和工具函数。 利用ref、reactive和computed进行响应式状态管理 实现自定义组合式函数以复用逻辑。 API 调用必须包含错误处理，所有 API 响应遵循统一格式 提交前必须通过全部测试 UI与样式 样式采用Tailwind css ","date":"2026-02-26T00:00:00Z","permalink":"https://muioo.github.io/p/%E5%89%8D%E7%AB%AFvue-ts%E5%BC%80%E5%8F%91%E8%A7%84%E5%88%99vue-ts%E5%B8%B8%E7%94%A8%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E4%BB%A5%E5%8F%8A%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6%E8%A7%84%E5%88%99/","title":"【前端Vue+Ts开发规则】Vue+Ts常用项目结构以及开发框架规则"},{"content":"爬虫基本框架 发送请求 以b站评论区为例子 1、找到对应的数据包\n2、应对反爬\n添加get请求参数 添加cookie 1 2 3 4 5 6 7 8 9 headers = { \u0026#34;cookie\u0026#34;:\u0026#34;buvid3=7178F649-B51E-3511-52F3-F35BBADC24AD07347infoc; b_nut=1766219807; _uuid=7885107F6-10532-5A53-3DE1-49762D4BF86A89782infoc; bmg_af_switch=1; bmg_src_def_domain=i1.hdslb.com; buvid4=D98C240B-1FA4-8D02-C9B3-DD7693AFC8DF90828-025122016-lD92PdnjNqMMVtStsO1Yaw%3D%3D; rpdid=|(J|lmkkkuJ|0J\u0026#39;u~YYkkul~u; DedeUserID=123152920; DedeUserID__ckMd5=14a440f25b863dcc; theme-tip-show=SHOWED; theme-avatar-tip-show=SHOWED; CURRENT_QUALITY=0; PEA_AU=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJiaWQiOjEyMzE1MjkyMCwicGlkIjozNzUyOTM4LCJleHAiOjE4MDIyNzAyMzAsImlzcyI6InRlc3QifQ.-Ki8KKoVKdTUYDeIL6zR10rl5t-RlYiDsIiLLJjK3ik; fingerprint=a4e5b37b8876bfe9b2ac49851b255d83; buvid_fp_plain=undefined; buvid_fp=a4e5b37b8876bfe9b2ac49851b255d83; bsource=search_bing; bp_t_offset_123152920=1167965937526112256; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NzEwMzc2MjMsImlhdCI6MTc3MDc3ODM2MywicGx0IjotMX0.YmqjaqFACN6HQkPvqSSujttyA0mmbQgrWYeHlb2Bhbg; bili_ticket_expires=1771037563; SESSDATA=8a9db992%2C1786523177%2C39356%2A21CjByGR1LZxP1_8VO6q6NMNeD7uaHBtFysH7X6KMHQD7BNzjXNZn8xey1B5Kzzpt74q0SVm14c1U4VkpZMUhkYl9uNnpjZGMyUmNibnc2NUs2eUdMS0kzSlZRT3hMOU1WZGR4ckJjMmx5NGwwcHZnVlBESnFQRkp3TWpSdUoyb3NLSndxUnBEOU9RIIEC; bili_jct=3900ae6a5e204b3a0afb02e5dbbb4a40; home_feed_column=5; browser_resolution=1536-695; sid=4j8dvybo; CURRENT_FNVAL=4048; b_lsid=C0839A2E_19C56CDB713\u0026#34;, \u0026#34;User-agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\u0026#34; } # get 查询参数 data = { \u0026#39;spm_id_from\u0026#39; : \u0026#39;333.337.search-card.all.click\u0026#39;, \u0026#39;vd_source\u0026#39; : \u0026#39;18d0a338800faafff98255fb11dc47ba\u0026#39; } 3、发送请求\n1 response = request.get(url=url,params=data,headers=headers) 解析数据 json_data 字典取值 1 2 3 4 5 6 7 8 9 10 11 12 13 import requests url = \u0026#34;https://www.bilibili.com/video/BV1K9Hse8EwM/?spm_id_from=333.337.search-card.all.click\u0026amp;vd_source=18d0a338800faafff98255fb11dc47ba\u0026#34; headers = { \u0026#34;cookie\u0026#34;:\u0026#34;buvid3=7178F649-B51E-3511-52F3-F35BBADC24AD07347infoc; b_nut=1766219807; _uuid=7885107F6-10532-5A53-3DE1-49762D4BF86A89782infoc; bmg_af_switch=1; bmg_src_def_domain=i1.hdslb.com; buvid4=D98C240B-1FA4-8D02-C9B3-DD7693AFC8DF90828-025122016-lD92PdnjNqMMVtStsO1Yaw%3D%3D; rpdid=|(J|lmkkkuJ|0J\u0026#39;u~YYkkul~u; DedeUserID=123152920; DedeUserID__ckMd5=14a440f25b863dcc; theme-tip-show=SHOWED; theme-avatar-tip-show=SHOWED; CURRENT_QUALITY=0; PEA_AU=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJiaWQiOjEyMzE1MjkyMCwicGlkIjozNzUyOTM4LCJleHAiOjE4MDIyNzAyMzAsImlzcyI6InRlc3QifQ.-Ki8KKoVKdTUYDeIL6zR10rl5t-RlYiDsIiLLJjK3ik; fingerprint=a4e5b37b8876bfe9b2ac49851b255d83; buvid_fp_plain=undefined; buvid_fp=a4e5b37b8876bfe9b2ac49851b255d83; bsource=search_bing; bp_t_offset_123152920=1167965937526112256; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NzEwMzc2MjMsImlhdCI6MTc3MDc3ODM2MywicGx0IjotMX0.YmqjaqFACN6HQkPvqSSujttyA0mmbQgrWYeHlb2Bhbg; bili_ticket_expires=1771037563; SESSDATA=8a9db992%2C1786523177%2C39356%2A21CjByGR1LZxP1_8VO6q6NMNeD7uaHBtFysH7X6KMHQD7BNzjXNZn8xey1B5Kzzpt74q0SVm14c1U4VkpZMUhkYl9uNnpjZGMyUmNibnc2NUs2eUdMS0kzSlZRT3hMOU1WZGR4ckJjMmx5NGwwcHZnVlBESnFQRkp3TWpSdUoyb3NLSndxUnBEOU9RIIEC; bili_jct=3900ae6a5e204b3a0afb02e5dbbb4a40; home_feed_column=5; browser_resolution=1536-695; sid=4j8dvybo; CURRENT_FNVAL=4048; b_lsid=C0839A2E_19C56CDB713\u0026#34;, \u0026#34;User-agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\u0026#34; } response = requests.get(url=url, headers=headers) # .content .text 获取html数据 .json 获取json数据 print(response.content) 保存数据 crawl4ai爬虫库 安装 1 2 3 pip install crawl4ai # crawl4ai需要依赖playwright python -m playwright install --with-deps chromium 使用 简单运行 1 2 3 4 5 6 7 8 9 10 11 12 import asyncio from crawl4ai import * async def main(): async with AsyncWebCrawler() as crawler: result = await crawler.arun( url=\u0026#34;https://next.xxx.com/xxx\u0026#34;, ) print(result.markdown) if __name__ == \u0026#34;__main__\u0026#34;: asyncio.run(main()) scrapy框架 ","date":"2026-02-14T00:00:00Z","permalink":"https://muioo.github.io/p/%E7%88%AC%E8%99%AB%E5%9F%BA%E6%9C%AC%E6%96%B9%E6%B3%95%E7%88%AC%E8%99%ABrequest%E5%9F%BA%E6%9C%AC%E6%A1%86%E6%9E%B6--scrapy%E6%A1%86%E6%9E%B6--crawl4ai/","title":"【爬虫基本方法】爬虫request基本框架 + scrapy框架 + crawl4ai"},{"content":"","date":"2026-02-08T00:00:00Z","permalink":"https://muioo.github.io/p/stm32ic%E9%80%9A%E4%BF%A1/","title":"【stm32】I²C通信"},{"content":"GPIO的工作方式 4种输入模式 输入浮空 输入上拉\n输入下拉\n模拟输入\n4种输出模式 开漏输出\n开口复用功能\n推挽输出\n推完复用功能\n串口打印数据 vscode重写\n1 2 3 4 5 int __io_putchar(int ch) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t*)\u0026amp;ch, 1, HAL_MAX_DELAY); return ch; } Keil5重写\n","date":"2026-02-08T00:00:00Z","permalink":"https://muioo.github.io/p/stm32%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1/","title":"【stm32】串口通信"},{"content":"Claude Code安装 1 2 # 前提环境node -v npm -v git -v npm install -g @anthropic-ai/claude-code ","date":"2026-02-07T00:00:00Z","image":"https://muioo.github.io/p/claude-code%E5%AE%89%E8%A3%85claude-code%E5%AE%89%E8%A3%85/page_hu_325c4362077cfcb5.png","permalink":"https://muioo.github.io/p/claude-code%E5%AE%89%E8%A3%85claude-code%E5%AE%89%E8%A3%85/","title":"【Claude Code安装】Claude Code安装"},{"content":"Claude Code 使用笔记 更新版本 1 2 3 claude --version //更新 npm update -g @anthropic-ai/claude-code 常用命令以及文件配置 回到之前的对话 1 2 3 4 # cmd中 claude -c # 回到之前的对话 # Claude code中 /rewind settings.json 配置api key 1 2 3 4 5 6 { \u0026#34;env\u0026#34;: { \u0026#34;ANTHROPIC_BASE_URL\u0026#34;: \u0026#34;https://mg.aid.pub/claude-proxy\u0026#34;, \u0026#34;ANTHROPIC_AUTH_TOKEN\u0026#34;: \u0026#34;填写你的 APIKey\u0026#34; } } claude code项目开发（项目结构） 1 2 3 4 5 6 7 8 9 your-project/ ├── .claude/ │ ├── CLAUDE.md # Main project instructions │ └── rules/ │ ├── code-style.md # Code style guidelines │ ├── testing.md # Testing conventions │ └── security.md # Security requirements │ └──skills │\t└── ... 也可以设置为子目录的形式 1 2 3 4 5 6 7 8 .claude/rules/ ├── frontend/ │ ├── react.md │ └── styles.md ├── backend/ │ ├── api.md │ └── database.md └── general.md .claude目录不要上传到github\n一些常用命令\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /init — 初始化CLAUDE.md，新项目第一件事 /compact — 压缩上下文，长对话续命神器 /model — 切模型，简单haiku，硬骨头opus /clear — 清空对话重新来 /resume — 恢复之前的对话 /memory — 编辑记忆文件，跨会话记忆 /add-dir — 添加工作目录 /mcp — 管理MCP服务器 /export — 导出对话到文件 /vim — 切换Vim模式 🥊【进阶命令·按需使用】 /cost — 看当前花了多少钱 /context — 可视化上下文使用情况 /review — 智能代码审查 /agents — 创建和管理智能体 /hooks — 管理工具事件钩子 /config — 打开配置面板 /doctor — 诊断安装问题 /status — 查看版本、模型、账户状态 /security-review — 安全审查待提交代码 /pr-comments — 获取GitHub PR评论 🎯【快捷键·效率翻倍】 Shift+Tab — 一键接受所有变更（最常用！） ESC — 中断执行，跑偏了立刻刹车 ESC+ESC — 跳转到之前的消息 Cmd+K — 打开命令面板 Cmd+B — 后台执行命令 Cmd+O — 展开/折叠过程信息 @文件名 — 引用文件 !命令 — 直接执行bash命令 [#信息](javascript:;) — 写入CLAUDE.md记忆 Control+V — 粘贴图片 让claudecode获得最高权限 1 claude --dangerously-skip-permissions MCP配置 mcp1: figma mcp 1 2 claude mcp add --transport http figma https://mcp.figma.com/mcp # 获取api-key mcp2:开源 Figma-Context-MCP（推荐） 对于预算有限或需要频繁使用的开发者，Figma-Context-MCP 是更好的选择。\n注意：使用时 在所在的项目添加mcp 配置步骤 步骤 1：全局安装依赖\n1 npm install -g figma-developer-mcp 步骤 2：创建配置文件\n在项目根目录创建 .mcp.json 文件（或修改 ~/.claude.json 以实现全局配置），添加以下内容：\n1 2 3 4 5 6 7 8 9 10 11 12 13 { \u0026#34;mcpServers\u0026#34;: { \u0026#34;figma-developer-mcp\u0026#34;: { \u0026#34;command\u0026#34;: \u0026#34;figma-developer-mcp\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;--stdio\u0026#34; ], \u0026#34;env\u0026#34;: { \u0026#34;FIGMA_API_KEY\u0026#34;: \u0026#34;your-figma-mcp-key\u0026#34; } } } } 配置说明：\nfigma-developer-mcp：自定义别名，避免与官方 MCP 冲突 FIGMA_API_KEY：需要在 Figma 设置中生成个人访问令牌 步骤 3：重启并授权\n配置完成后，重启 Claude Code，同意使用该 MCP：\n使用教程 使用figma 开发前端样式页面\n1、先在figma中选中页面样式\n","date":"2026-02-07T00:00:00Z","image":"https://muioo.github.io/p/claude-code%E5%91%BD%E4%BB%A4claude-code%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4--skills--mcp/page_hu_325c4362077cfcb5.png","permalink":"https://muioo.github.io/p/claude-code%E5%91%BD%E4%BB%A4claude-code%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4--skills--mcp/","title":"【Claude Code命令】Claude Code（常用命令 + skills + mcp）"},{"content":"STM32开发笔记 STM32项目搭建 编译工具 cmake(安装教程）\n1、安装后配置环境变量\nARM-GCC 编译器\n1、安装链接\n2、配置环境变量，检验安装成功\n1 arm-none-eabi-gcc -v Ninja构建工具\n1、安装链接\n2、配置环境变量\nIDE VSCODE\n1、安装插件STM32CubeIDE for Visual Studio Code\n2、安装插件STM32 VS Code Extension，这个插件会自动下载ARM-GCC 编译器和ninja构建工具\n3、之后打开项目文件夹即可打开cubemx创建的项目\nSTM32cubemx（用于创建项目）\n1、下载地址\n2、创建教程\n烧录工具 STM32cubeprogrammer\n1、下载地址\n连接成功演示图\n​\t连接成功演示 注意事项：烧录时，启动模式得设置为boot0=1，boot1=0，boot0接高电平boot1接GND 串口测试工具 SSCOM 使用CUBEMX初始化FreeRTOS项目 FreeRTOS移植教程 FreeRTOS 可以理解为并发（多核同时执行多个任务） 不可以理解为并行（需要单核切换来执行多个任务） 代码结构 主要开发在core/Src和core/Inc目录中，Src目录中用来写具体功能，Inc定义函数和引脚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 USV_BASE/ ├── Core/ # STM32CubeMX生成（不修改） │ ├── Inc/ │ │ ├── main.h │ │ ├── can.h # CubeMX配置 │ │ ├── usart.h # CubeMX配置 │ │ └── ... │ └── Src/ │ ├── freertos.c # 任务创建（尽量不要与STM32CubeMX生成的任务代码冲突） │ ├── can.c # CAN中断回调（修改USER CODE区） │ ├── usart.c # UART中断回调（修改USER CODE区） │ └── ... │ ├── App/ # 应用代码 │ ├── Inc/ │ │ ├── app_config.h # 系统配置 │ │ ├── app_types.h # 公共数据类型 │ │ └── app_def.h # 宏定义 │ │ │ ├── Driver/ # 驱动层（2个模块） │ │ ├── Inc/ │ │ │ ├── driver_can_radar.h │ │ │ └── driver_sbus.h │ │ └── Src/ │ │ ├── driver_can_radar.c │ │ └── driver_sbus.c │ │ │ ├── Algorithm/ # 算法模块（可选扩展） │ │ ├── Inc/ │ │ │ └── algo_avoid.h # 避障算法 │ │ └── Src/ │ │ └── algo_avoid.c │ │ │ └── Task/ # 任务层（3个任务） │ ├── Inc/ │ │ ├── task_radar.h # 雷达+避障 │ │ ├── task_sbus.h # SBUS通信 │ │ └── task_control.h # 控制决策 │ └── Src/ │ ├── task_radar.c │ ├── task_sbus.c │ └── task_control.c │ └── Docs/ # 文档 └── ArchitectureDesign_Simple.md 修改CUBEMX中定义的引脚会自动修改宏定义和引脚 遇到的问题 1、stlink烧录问题 使用usb口给开发板供电，解决如下问题 ​\tST-LINK烧录问题 ST-LINK烧录\n驱动更新：需要重启开发板或断开数据线才能识别\n烧录时要按下reset键 点击连接后松开reset，否则会遇到如下问题\n​\t","date":"2026-02-03T00:00:00Z","permalink":"https://muioo.github.io/p/stm32%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA%E5%92%8Cfreertos/","title":"【stm32】项目搭建和FreeRTOS"},{"content":"VSCODE启动没有反应 解决方案： 1）查看vscode目录下debug.log日志 意思为：VS Code 读不到它自带的 ICU 国际化数据文件。这个 ICU 报错几乎都是 安装目录里的资源文件缺失/损坏 或者 目录权限/杀软隔离 导致 Code.exe 读不到它的 icudtl.dat 等文件。 2）先确认是不是“用户配置/插件”把它搞崩了 1 2 cmd中： code --disable-extensions 如果这样能打开：说明是插件或用户配置损坏。之后进VS Code后逐个禁用插件试试。打不开接着往下尝试： 3）检查是否是环境变量/路径的影响 1 where code 结果无输出。\ncmd 里能运行 code 但 VS Code 打不开，也就是说，code 这个入口文件还在某个目录里，但 VS Code 本体/资源（ICU）坏了或路径断了。\n然后把“真正调用的 code 在哪”找出来，再直接修复。\n查找code指向哪里： 1 Get-Command code -All 如果它提示找不到命令，那说明 PS 环境找不到 code，可能是cmd里用了不同的 PATH / 或者跑的是某个残留的code.cmd。确保你vscode里的bin目录下是正确的code.cmd就行了。\n最关键的一步来了，我是在这一步找到了问题所在：\n检查关键资源文件是否还在 注意：路径要用你的vscode安装目录里bin文件夹所在同级路径。如上图，我的bin目录是在D:\\vscode\\Microsoft VS Code下。 1 2 3 4 5 6 7 8 9 Test-Path \u0026#34;D:\\App\\Microsoft VS Code\\icudtl.dat\u0026#34; Test-Path \u0026#34;D:\\App\\Microsoft VS Code\\resources.pak\u0026#34; Test-Path \u0026#34;D:\\App\\Microsoft VS Code\\libEGL.dll\u0026#34; Test-Path \u0026#34;D:\\App\\Microsoft VS Code\\libGLESv2.dll\u0026#34; 我的输出： false false false false 这些关键资源按理来说是要放在bin同级目录下，Code.exe 启动时会在根目录找icudtl.dat/resources.pak/libEGL.dll/libGLESv2.dll。但是找不到，说明路径错了。\n所以破案了：我的 VS Code 安装目录结构被“挪了一层”——关键资源文件都跑到 D:\\vscode\\Microsoft VS Code_\\ 下面了，而 Code.exe 启动时会在根目录找 icudtl.dat / resources.pak / libEGL.dll / libGLESv2.dll，所以直接报 ICU 相关错误。\n所以把这些文件（以及同目录下其它资源）移回根目录即可：\n1 2 3 4 5 6 7 8 $root = \u0026#34;D:\\vscode\\Microsoft VS Code\u0026#34; $under = Join-Path $root \u0026#34;_\u0026#34; # 1) 备份一下（可选但推荐） Copy-Item -Recurse -Force $root \u0026#34;$root.bak_$(Get-Date -Format yyyyMMdd_HHmmss)\u0026#34; # 2) 把 _\\ 里的所有内容移动到根目录（遇到同名会覆盖） Move-Item -Force \u0026#34;$under\\*\u0026#34; $root # 3) 尝试启动 \u0026amp; \u0026#34;$root\\Code.exe\u0026#34; ","date":"2026-02-03T00:00:00Z","permalink":"https://muioo.github.io/p/vscode-%E9%97%AE%E9%A2%98vscode%E5%B7%A5%E5%85%B7%E9%97%AE%E9%A2%98/","title":"【VSCODE 问题】VSCODE工具问题"},{"content":"hugo搭建静态网站（样式修改和新增功能基本就是对照theme的目录和文件来修覆盖) 下载hugo.exe\n下载主题（Hugo Themes）到hugo项目根目录的theme中\n删除主题的版本号后缀，将exampleSite/content目录和配置文件移动到根目录\n删除content中的rich-content\n自定义firstblog\\layouts_default目录的作用 基础模板，所有页面（文章、首页、归档等）都会继承它 添加背景图片 在static\\images中添加图片 在assets\\scss中custom.html添加样式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #dark-mode-toggle { margin-top: auto; color: var(--body-text-color); display: flex; align-items: center; cursor: pointer; gap: var(--menu-icon-separation); margin-bottom: 30px; } body { background-image: url(\u0026#34;/images/background2.jpg\u0026#34;) !important; /* 对应static/images/bg.jpg */ background-size: cover !important; /* 背景图覆盖整个页面 */ background-position: center !important; /* 背景图居中显示 */ background-attachment: fixed !important; /* 背景图固定（滚动时不跟随） */ background-repeat: no-repeat !important; /* 禁止背景图重复 */ /* 可选：添加半透明遮罩，避免背景干扰文字 */ background-color: rgba(255, 255, 255, 0.8) !important; background-blend-mode: overlay !important; } /* 修复文章背景首次加载透明问题 */ .article-page .main-article { background: #fff !important; } /* Swup 过渡期间保持背景色 */ html.is-animating .main-article { background: #fff !important; } /* 为浅色和深色模式分别设置 */ :root .article-page .main-article { background: #fff !important; } [data-scheme=\u0026#34;dark\u0026#34;] .article-page .main-article { background: #424242 !important; } /* 修复图片首次加载超出内容框的问题 */ .article-content figure { max-width: 100%; overflow: hidden; } .article-content figure img { max-width: 100%; height: auto; display: block; } 如果遇到文章内容背景加载不出来的情况， layouts/partials/head/custom.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 \u0026lt;script\u0026gt; /*! * Live2D Widget * https://github.com/stevenjoezhang/live2d-widget */ // Recommended to use absolute path for live2d_path parameter // live2d_path 参数建议使用绝对路径 const live2d_path = \u0026#39;https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0-rc.6/dist/\u0026#39;; // const live2d_path = \u0026#39;/dist/\u0026#39;; // Method to encapsulate asynchronous resource loading // 封装异步加载资源的方法 function loadExternalResource(url, type) { return new Promise((resolve, reject) =\u0026gt; { let tag; if (type === \u0026#39;css\u0026#39;) { tag = document.createElement(\u0026#39;link\u0026#39;); tag.rel = \u0026#39;stylesheet\u0026#39;; tag.href = url; } else if (type === \u0026#39;js\u0026#39;) { tag = document.createElement(\u0026#39;script\u0026#39;); tag.type = \u0026#39;module\u0026#39;; tag.src = url; } if (tag) { tag.onload = () =\u0026gt; resolve(url); tag.onerror = () =\u0026gt; reject(url); document.head.appendChild(tag); } }); } (async () =\u0026gt; { // If you are concerned about display issues on mobile devices, you can use screen.width to determine whether to load // 如果担心手机上显示效果不佳，可以根据屏幕宽度来判断是否加载 // if (screen.width \u0026lt; 768) return; // Avoid cross-origin issues with image resources // 避免图片资源跨域问题 const OriginalImage = window.Image; window.Image = function(...args) { const img = new OriginalImage(...args); img.crossOrigin = \u0026#34;anonymous\u0026#34;; return img; }; window.Image.prototype = OriginalImage.prototype; // Load waifu.css and waifu-tips.js // 加载 waifu.css 和 waifu-tips.js await Promise.all([ loadExternalResource(live2d_path + \u0026#39;waifu.css\u0026#39;, \u0026#39;css\u0026#39;), loadExternalResource(live2d_path + \u0026#39;waifu-tips.js\u0026#39;, \u0026#39;js\u0026#39;) ]); // For detailed usage of configuration options, see README.en.md // 配置选项的具体用法见 README.md initWidget({ waifuPath: live2d_path + \u0026#39;waifu-tips.json\u0026#39;, // cdnPath: \u0026#39;https://fastly.jsdelivr.net/gh/fghrsh/live2d_api/\u0026#39;, cubism2Path: live2d_path + \u0026#39;live2d.min.js\u0026#39;, cubism5Path: \u0026#39;https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js\u0026#39;, tools: [\u0026#39;hitokoto\u0026#39;, \u0026#39;asteroids\u0026#39;, \u0026#39;switch-model\u0026#39;, \u0026#39;switch-texture\u0026#39;, \u0026#39;photo\u0026#39;, \u0026#39;info\u0026#39;, \u0026#39;quit\u0026#39;], logLevel: \u0026#39;warn\u0026#39;, drag: false, }); })(); console.log(`\\n%cLive2D%cWidget%c\\n`, \u0026#39;padding: 8px; background: #cd3e45; font-weight: bold; font-size: large; color: white;\u0026#39;, \u0026#39;padding: 8px; background: #ff5450; font-size: large; color: #eee;\u0026#39;, \u0026#39;\u0026#39;); /* く__,.ヘヽ. / ,ー､ 〉 ＼ \u0026#39;, !-─‐-i / /´ ／｀ｰ\u0026#39; L/／｀ヽ､ / ／, /| , , \u0026#39;, ｲ / /-‐/ ｉ L_ ﾊ ヽ! i ﾚ ﾍ 7ｲ｀ﾄ ﾚ\u0026#39;ｧ-ﾄ､!ハ| | !,/7 \u0026#39;0\u0026#39; ´0iソ| | |.从\u0026#34; _ ,,,, / |./ | ﾚ\u0026#39;| i＞.､,,__ _,.イ / .i | ﾚ\u0026#39;| | / k_７_/ﾚ\u0026#39;ヽ, ﾊ. | | |/i 〈|/ i ,.ﾍ | i | .|/ / ｉ： ﾍ! ＼ | kヽ\u0026gt;､ﾊ _,.ﾍ､ /､! !\u0026#39;〈//｀Ｔ´\u0026#39;, ＼ ｀\u0026#39;7\u0026#39;ｰr\u0026#39; ﾚ\u0026#39;ヽL__|___i,___,ンﾚ|ノ ﾄ-,/ |___./ \u0026#39;ｰ\u0026#39; !_,.: */ \u0026lt;/script\u0026gt; \u0026lt;!-- 关键 CSS：防止首次加载时文章背景透明 --\u0026gt; \u0026lt;style\u0026gt; /* 确保文章容器始终有白色背景 */ .article-page .main-article, main .main-article { background: #fff !important; } /* 深色模式 */ [data-scheme=\u0026#34;dark\u0026#34;] .article-page .main-article, [data-scheme=\u0026#34;dark\u0026#34;] main .main-article { background: #424242 !important; } \u0026lt;/style\u0026gt; \u0026lt;!-- 自定义样式：将看板娘移动到右下角 --\u0026gt; \u0026lt;style\u0026gt; #waifu { left: auto !important; right: 0 !important; bottom: 0 !important; } #waifu-tips { left: auto !important; right: 0 !important; } #waifu-tool { left: auto !important; right: 0 !important; } \u0026lt;/style\u0026gt; 文章底部的计数 效果图 底部运行统计效果 新建baseof.html在layouts/_default目录下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;{{ .Site.LanguageCode }}\u0026#34; dir=\u0026#34;{{ default `ltr` .Language.LanguageDirection }}\u0026#34;\u0026gt; \u0026lt;head\u0026gt; {{- partial \u0026#34;head/head.html\u0026#34; . -}} {{- block \u0026#34;head\u0026#34; . -}}{{ end }} \u0026lt;!-- giscus 预优化：DNS 预解析 + 预连接 --\u0026gt; \u0026lt;link rel=\u0026#34;dns-prefetch\u0026#34; href=\u0026#34;https://giscus.app\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://giscus.app\u0026#34; crossorigin\u0026gt; \u0026lt;!-- Swup for seamless page transitions --\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/swup@4\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;style\u0026gt; /* Swup transition styles */ .transition-fade { transition: opacity 0.3s; opacity: 1; } html.is-animating .transition-fade { opacity: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026#34;{{ block `body-class` . }}{{ end }}\u0026#34;\u0026gt; {{- partial \u0026#34;head/colorScheme\u0026#34; . -}} {{/* The container is wider when there\u0026#39;s any activated widget */}} {{- $hasWidget := false -}} {{- range .Site.Params.widgets -}} {{- if gt (len .) 0 -}} {{- $hasWidget = true -}} {{- end -}} {{- end -}} \u0026lt;!-- Swup transition container --\u0026gt; \u0026lt;div id=\u0026#34;swup\u0026#34; class=\u0026#34;transition-fade\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;container main-container flex on-phone--column {{ if $hasWidget }}extended{{ else }}compact{{ end }}\u0026#34;\u0026gt; {{- block \u0026#34;left-sidebar\u0026#34; . -}} {{ partial \u0026#34;sidebar/left.html\u0026#34; . }} {{- end -}} {{- block \u0026#34;right-sidebar\u0026#34; . -}}{{ end }} \u0026lt;main class=\u0026#34;main full-width\u0026#34;\u0026gt; {{- block \u0026#34;main\u0026#34; . }}{{- end }} \u0026lt;/main\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Audio player outside Swup container to prevent reload --\u0026gt; {{ partial \u0026#34;footer/aplayer.html\u0026#34; . }} \u0026lt;!-- 全局 giscus 状态管理（与 giscus.html 配合） --\u0026gt; \u0026lt;script\u0026gt; (function() { // 全局 giscus 状态 window.giscusState = { loaded: false, ready: false }; // 监听 giscus 加载完成事件 document.addEventListener(\u0026#39;giscus:loaded\u0026#39;, function() { window.giscusState.ready = true; console.log(\u0026#39;[Giscus] 全局状态已更新为 ready\u0026#39;); }); })(); \u0026lt;/script\u0026gt; \u0026lt;!-- Other footer scripts --\u0026gt; {{ partialCached \u0026#34;footer/components/script.html\u0026#34; . }} {{ partialCached \u0026#34;footer/components/custom-font.html\u0026#34; . }} \u0026lt;!-- giscus 评论脚本（放在 body 末尾确保执行） --\u0026gt; \u0026lt;script\u0026gt; (function() { \u0026#39;use strict\u0026#39;; console.log(\u0026#39;[GISCUS-MAIN] Script loaded\u0026#39;); var giscusConfig = { src: \u0026#39;https://giscus.app/client.js\u0026#39;, repo: \u0026#39;muioo/muioo.github.io\u0026#39;, repoId: \u0026#39;R_kgDOQ_pqAA\u0026#39;, category: \u0026#39;Announcements\u0026#39;, categoryId: \u0026#39;DIC_kwDOQ_pqAM4C2CEc\u0026#39;, mapping: \u0026#39;pathname\u0026#39;, strict: \u0026#39;0\u0026#39;, reactionsEnabled: \u0026#39;1\u0026#39;, emitMetadata: \u0026#39;0\u0026#39;, inputPosition: \u0026#39;bottom\u0026#39;, theme: \u0026#39;preferred_color_scheme\u0026#39;, lang: \u0026#39;zh-CN\u0026#39; }; var loaded = false; var observer = null; var timeoutId = null; function loadGiscus(container) { if (loaded) return; loaded = true; console.log(\u0026#39;[GISCUS-MAIN] Loading giscus...\u0026#39;); if (observer) { observer.disconnect(); observer = null; } if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } var script = document.createElement(\u0026#39;script\u0026#39;); script.src = giscusConfig.src; script.async = true; script.crossOrigin = \u0026#39;anonymous\u0026#39;; for (var key in giscusConfig) { if (key !== \u0026#39;src\u0026#39; \u0026amp;\u0026amp; giscusConfig.hasOwnProperty(key)) { var dataKey = \u0026#39;data-\u0026#39; + key.replace(/([A-Z])/g, \u0026#39;-$1\u0026#39;).toLowerCase(); script.setAttribute(dataKey, giscusConfig[key]); } } script.onload = function() { console.log(\u0026#39;[GISCUS-MAIN] giscus loaded successfully\u0026#39;); container.classList.remove(\u0026#39;giscus-placeholder\u0026#39;); initFeatures(); }; script.onerror = function() { console.error(\u0026#39;[GISCUS-MAIN] Failed to load giscus\u0026#39;); container.innerHTML = \u0026#39;\u0026lt;p style=\u0026#34;color: #ef4444; text-align: center; padding: 2rem;\u0026#34;\u0026gt;评论加载失败，请刷新页面重试\u0026lt;/p\u0026gt;\u0026#39;; }; container.innerHTML = \u0026#39;\u0026#39;; container.appendChild(script); } function initFeatures() { var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.attributeName === \u0026#39;class\u0026#39;) { var iframe = document.querySelector(\u0026#39;iframe[src*=\u0026#34;giscus.app\u0026#34;]\u0026#39;); if (iframe) { var isDark = document.body.classList.contains(\u0026#39;dark\u0026#39;); var theme = isDark ? \u0026#39;dark\u0026#39; : \u0026#39;light\u0026#39;; try { iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: theme } } }, \u0026#39;https://giscus.app\u0026#39;); } catch (e) { // Ignore cross-origin errors on first load } } } }); }); observer.observe(document.body, { attributes: true, attributeFilter: [\u0026#39;class\u0026#39;] }); setTimeout(function() { var iframe = document.querySelector(\u0026#39;iframe[src*=\u0026#34;giscus.app\u0026#34;]\u0026#39;); if (iframe) { var isDark = document.body.classList.contains(\u0026#39;dark\u0026#39;); var theme = isDark ? \u0026#39;dark\u0026#39; : \u0026#39;light\u0026#39;; try { iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: theme } } }, \u0026#39;https://giscus.app\u0026#39;); } catch (e) {} } }, 1000); } function isNearViewport(el, margin) { var rect = el.getBoundingClientRect(); var windowHeight = window.innerHeight || document.documentElement.clientHeight; return rect.top \u0026lt;= windowHeight + margin; } function initLazyLoad() { console.log(\u0026#39;[GISCUS-MAIN] initLazyLoad called\u0026#39;); var container = document.getElementById(\u0026#39;giscus-container\u0026#39;); if (!container) { console.log(\u0026#39;[GISCUS-MAIN] Container not found, will retry\u0026#39;); setTimeout(initLazyLoad, 100); return; } console.log(\u0026#39;[GISCUS-MAIN] Container found\u0026#39;); if (isNearViewport(container, 200)) { console.log(\u0026#39;[GISCUS-MAIN] Container in viewport, loading immediately\u0026#39;); loadGiscus(container); return; } if (\u0026#39;IntersectionObserver\u0026#39; in window) { console.log(\u0026#39;[GISCUS-MAIN] Using IntersectionObserver\u0026#39;); observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { console.log(\u0026#39;[GISCUS-MAIN] IntersectionObserver triggered\u0026#39;); observer.disconnect(); observer = null; loadGiscus(container); } }); }, { root: null, rootMargin: \u0026#39;200px\u0026#39;, threshold: 0 }); observer.observe(container); timeoutId = setTimeout(function() { if (!loaded) { console.log(\u0026#39;[GISCUS-MAIN] Timeout fallback triggered\u0026#39;); if (observer) { observer.disconnect(); observer = null; } loadGiscus(container); } }, 3000); } else { console.log(\u0026#39;[GISCUS-MAIN] IntersectionObserver not supported, loading immediately\u0026#39;); loadGiscus(container); } } function init() { console.log(\u0026#39;[GISCUS-MAIN] init() called\u0026#39;); initLazyLoad(); } function resetForNewPage() { console.log(\u0026#39;[GISCUS-MAIN] Swup page change, resetting\u0026#39;); loaded = false; var container = document.getElementById(\u0026#39;giscus-container\u0026#39;); if (container) { container.classList.add(\u0026#39;giscus-placeholder\u0026#39;); container.innerHTML = \u0026#39;\u0026lt;div class=\u0026#34;giscus-loading\u0026#34;\u0026gt;\u0026lt;div class=\u0026#34;loading-spinner\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;p class=\u0026#34;loading-text\u0026#34;\u0026gt;评论区加载中...\u0026lt;/p\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;; } setTimeout(function() { initLazyLoad(); }, 100); } if (document.readyState === \u0026#39;loading\u0026#39;) { console.log(\u0026#39;[GISCUS-MAIN] Waiting for DOMContentLoaded\u0026#39;); document.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, init); } else { console.log(\u0026#39;[GISCUS-MAIN] DOM ready, calling init\u0026#39;); init(); } document.addEventListener(\u0026#39;swup:page:view\u0026#39;, resetForNewPage); console.log(\u0026#39;[GISCUS-MAIN] Script registered\u0026#39;); })(); \u0026lt;/script\u0026gt; \u0026lt;!-- Swup initialization --\u0026gt; \u0026lt;script\u0026gt; const swup = new Swup(); \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script\u0026gt; // ✅ 定义更新运行天数的函数 function updateRunningDays() { const BLOG_LAUNCH_DATE = \u0026#39;2026-01-22\u0026#39;; const startDate = new Date(BLOG_LAUNCH_DATE + \u0026#39;T00:00:00Z\u0026#39;); const today = new Date(); const utcToday = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate())); const timeDiff = utcToday - startDate; const runningDays = Math.max(0, Math.floor(timeDiff / (1000 * 60 * 60 * 24))); const daysElement = document.getElementById(\u0026#39;running-days\u0026#39;); if (daysElement) { daysElement.textContent = runningDays; } } // 🔄 首次加载时立即执行 updateRunningDays(); // 🔄 监听 Swup 页面切换完成事件 document.addEventListener(\u0026#39;swup:page:view\u0026#39;, () =\u0026gt; { updateRunningDays(); }); \u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; layouts\\partials\\footer目录下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 {{- $ThemeVersion := \u0026#34;3.33.0\u0026#34; -}} \u0026lt;footer class=\u0026#34;site-footer\u0026#34;\u0026gt; \u0026lt;section class=\u0026#34;copyright\u0026#34;\u0026gt; \u0026amp;copy; {{ if and (.Site.Params.footer.since) (ne .Site.Params.footer.since (int (now.Format \u0026#34;2006\u0026#34;))) }} {{ .Site.Params.footer.since }} - {{ end }} {{ now.Format \u0026#34;2006\u0026#34; }} {{ default .Site.Title .Site.Copyright }} \u0026lt;/section\u0026gt; \u0026lt;section class=\u0026#34;powerby\u0026#34;\u0026gt; {{- /* 计算统计数据 */ -}} {{- $postCount := len (where .Site.RegularPages \u0026#34;Type\u0026#34; \u0026#34;post\u0026#34;) -}} {{- $totalWords := 0 -}} {{- range where .Site.RegularPages \u0026#34;Type\u0026#34; \u0026#34;post\u0026#34; -}} {{- $totalWords = add $totalWords .WordCount -}} {{- end -}} {{- /* 动态生成统计文本 */ -}} \u0026lt;div id=\u0026#34;blog-stats\u0026#34; style=\u0026#34;margin-bottom: 5px;\u0026#34; data-launch-date=\u0026#34;2026-01-22\u0026#34;\u0026gt; 本站已稳定运行: \u0026lt;strong id=\u0026#34;running-days\u0026#34;\u0026gt;计算中...\u0026lt;/strong\u0026gt; 天 | 博客: \u0026lt;strong\u0026gt;{{ $postCount }}\u0026lt;/strong\u0026gt; 篇 | 字数: \u0026lt;strong\u0026gt;{{ $totalWords }}\u0026lt;/strong\u0026gt; 字 \u0026lt;/div\u0026gt; {{- $Generator := `\u0026lt;a href=\u0026#34;https://gohugo.io/\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener\u0026#34;\u0026gt;Hugo\u0026lt;/a\u0026gt;` -}} {{- $Theme := printf `\u0026lt;b\u0026gt;\u0026lt;a href=\u0026#34;https://github.com/CaiJimmy/hugo-theme-stack\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener\u0026#34; data-version=\u0026#34;%s\u0026#34;\u0026gt;Stack\u0026lt;/a\u0026gt;\u0026lt;/b\u0026gt;` $ThemeVersion -}} {{- $DesignedBy := `\u0026lt;a href=\u0026#34;https://jimmycai.com\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener\u0026#34;\u0026gt;Jimmy\u0026lt;/a\u0026gt;` -}} {{ T \u0026#34;footer.builtWith\u0026#34; (dict \u0026#34;Generator\u0026#34; $Generator) | safeHTML }} \u0026lt;br /\u0026gt; {{ T \u0026#34;footer.designedBy\u0026#34; (dict \u0026#34;Theme\u0026#34; $Theme \u0026#34;DesignedBy\u0026#34; $DesignedBy) | safeHTML }} \u0026lt;/section\u0026gt; \u0026lt;/footer\u0026gt; 看板娘功能 评论区功能 1、相关链接\n**giscus评论**按照流程注册giscus\n下载giscus评论到仓库中github\n2、在layouts\\partials\\comments目录下新建giscus.html、include.html覆盖掉下载的主题中的文件\ngiscus.html文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 {{/* DNS 预连接优化 */}} \u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://giscus.app\u0026#34; crossorigin\u0026gt; \u0026lt;link rel=\u0026#34;dns-prefetch\u0026#34; href=\u0026#34;https://giscus.app\u0026#34;\u0026gt; {{/* giscus 容器 */}} \u0026lt;div id=\u0026#34;giscus-container\u0026#34; class=\u0026#34;giscus-placeholder\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;giscus-loading\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;loading-spinner\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;p class=\u0026#34;loading-text\u0026#34;\u0026gt;评论区加载中...\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; {{/* 优化样式 */}} \u0026lt;style\u0026gt; .giscus-placeholder { min-height: 200px; display: flex; align-items: center; justify-content: center; } .giscus-loading { display: flex; flex-direction: column; align-items: center; gap: 1rem; padding: 2rem 1rem; color: var(--text-secondary, #666); transition: opacity 0.3s ease; } .loading-spinner { width: 32px; height: 32px; border: 3px solid var(--border-color, #e0e0e0); border-top-color: var(--accent-color, #3b82f6); border-radius: 50%; animation: giscus-spin 0.8s linear infinite; } @keyframes giscus-spin { to { transform: rotate(360deg); } } .loading-text { font-size: 0.875rem; opacity: 0.7; } .giscus-frame { min-height: 200px; width: 100%; } #giscus-container:not(.giscus-placeholder) { min-height: 400px; } /* 确保 giscus iframe 可见 */ #giscus-container iframe { width: 100% !important; min-height: 200px !important; border: none !important; } \u0026lt;/style\u0026gt; include.html文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 {{/* 评论功能主入口 - 懒加载版本 */}} {{ if .Params.comments | default true }} \u0026lt;section class=\u0026#34;comments-section\u0026#34; aria-label=\u0026#34;评论\u0026#34; data-comments-section\u0026gt; \u0026lt;div class=\u0026#34;comments-container\u0026#34;\u0026gt; \u0026lt;h2 class=\u0026#34;comments-title\u0026#34;\u0026gt;评论\u0026lt;/h2\u0026gt; {{ partial \u0026#34;comments/giscus.html\u0026#34; . }} \u0026lt;/div\u0026gt; \u0026lt;/section\u0026gt; \u0026lt;style\u0026gt; .comments-section { margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--border-color, #e0e0e0); } .comments-container { max-width: 100%; } .comments-title { font-size: 1.5rem; font-weight: 600; margin-bottom: 1.5rem; color: var(--text-primary, #1a1a1a); } #giscus-container { margin-top: 1rem; } body.dark .comments-title { color: var(--text-primary, #e0e0e0); } body.dark .loading-text { color: var(--text-secondary, #999); } body.dark .loading-spinner { border-color: var(--border-color, #333); border-top-color: var(--accent-color, #60a5fa); } \u0026lt;/style\u0026gt; {{ end }} 3、在_default目录中的baseof.html文件中加载上述文件（如果评论区加载不出来或者有问题可能是因为baseof.html文件中渲染的顺序有问题）\ngithub actions自动构建 创建.github/workflows/hugo.yml文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 name: Deploy Hugo site to Pages on: push: branches: - main # 或 master，根据你的主分支名 workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: \u0026#34;pages\u0026#34; cancel-in-progress: false defaults: run: shell: bash jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true # 如果你的主题是子模块，需要开启 fetch-depth: 0 - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: \u0026#39;latest\u0026#39; extended: true - name: Build run: hugo --minify - name: Setup Pages uses: actions/configure-pages@v4 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ./public deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ","date":"2026-01-27T00:00:00Z","permalink":"https://muioo.github.io/p/hugo-%E7%BD%91%E7%AB%99%E6%90%AD%E5%BB%BAhugo%E6%90%AD%E5%BB%BA%E9%9D%99%E6%80%81%E9%A1%B5%E9%9D%A2%E7%BD%91%E7%AB%99/","title":"【Hugo 网站搭建】Hugo搭建静态页面网站"},{"content":"项目部署 前端部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { \u0026#39;@\u0026#39;: path.resolve(__dirname, \u0026#39;./src\u0026#39;), }, }, server: { port: 8891, host: \u0026#39;0.0.0.0\u0026#39;, proxy: { // 转发所有以 /api 开头的接口请求 \u0026#39;/api\u0026#39;: { target: \u0026#39;http://localhost:8000\u0026#39;, changeOrigin: true, }, \u0026#39;/baike_img\u0026#39;: { target: \u0026#39;http://localhost:8000\u0026#39;, changeOrigin: true, } } } }) 后端部署 ","date":"2026-01-26T00:00:00Z","permalink":"https://muioo.github.io/p/vue-%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2%E7%AC%94%E8%AE%B0%E5%A6%82%E4%BD%95%E9%83%A8%E7%BD%B2vue%E9%A1%B9%E7%9B%AE%E5%88%B0linux%E6%9C%8D%E5%8A%A1%E5%99%A8/","title":"【VUE 项目部署笔记】如何部署vue项目到linux服务器"},{"content":"后端开发规则（fastapi） 您是一位精通 Python、FastAPI 和可扩展 API 开发的专家。 关键原则 编写简洁、技术性的回复，并提供准确的 Python 示例。\n使用函数式、声明式编程；尽可能避免使用类。\n优先使用迭代和模块化，避免代码重复。\n使用带有辅助动词的描述性变量名（例如 is_active、has_permission）。\n目录和文件使用小写字母和下划线（例如 routers/user_routes.py）。\n优先使用命名导出路由和工具函数。\n使用“接收对象，返回对象”（RORO）模式。\n目录结构采用\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 backend/ ├── api/\t# API路由层 ├── __init__.py # 创建FastAPI应用实例，注册路由 └── v1/ # API版本1 ├── __init__.py # 注册v1版本的所有路由 └── endpoints/ # 具体的端点模块 ├── login/ # 登录相关接口 ├── user/ # 用户管理接口 ├── role/ # 角色管理接口 └── permission/ # 权限管理接口 ├── controllers/ # 业务逻辑控制器层 ├── services/\t# 算法等 ├── core/\t# 核心功能模块 ├── security.py # JWT认证、用户身份验证、OAuth2配置 ├── crud.py # 通用CRUD操作基类 ├── dependencies.py # FastAPI依赖注入函数\t├── models/ # 数据库模型层 ├── schemas/ # 数据验证和序列化层 ├── settings/ ├── config.py # 配置管理，数据库连接配置，JWT密钥和算法配置，应用程序环境变量，第三方服务配置等 ├── utils/ # 工具函数库，密码加密和验证，JWT令牌生成和解析，数据序列化工具，其他通用工具函数 ├── main.py # 应用入口文件 └── __init__.py # 包初始化文件 Python/FastAPI 使用 def 定义纯函数，使用 async def 定义异步操作。 为所有函数签名使用类型提示。优先使用 Pydantic 模型而不是原始字典进行输入验证。 文件结构：导出的路由器、子路由、工具函数、静态内容、类型（模型、模式）。 避免在条件语句中使用不必要的大括号。 对于条件语句中的单行语句，省略大括号。 对于简单的条件语句，使用简洁的单行语法（例如 if condition: do_something()）。 错误处理和验证 优先处理错误和边缘情况： 在函数开头处理错误和边缘情况。 使用提前返回来处理错误条件，避免深度嵌套的 if 语句。 将正常路径放在函数末尾，以提高可读性。 避免不必要的 else 语句；使用 if-return 模式。 使用守卫子句提前处理前提条件和无效状态。 实现适当的错误日志记录和用户友好的错误消息。 使用自定义错误类型或错误工厂进行一致的错误处理。 依赖项 FastAPI Pydantic v2 异步数据库库，如 asyncpg 或 aiomysql SQLAlchemy 2.0（如果使用 ORM 功能） FastAPI 特定指南 使用函数式组件（普通函数）和 Pydantic 模型进行输入验证和响应模式。 使用声明式路由定义，并带有清晰的返回类型注解。 使用 def 定义同步操作，使用 async def 定义异步操作。 尽量减少 @app.on_event(\u0026quot;startup\u0026quot;) 和 @app.on_event(\u0026quot;shutdown\u0026quot;) 的使用；优先使用生命周期上下文管理器来管理启动和关闭事件。 使用中间件进行日志记录、错误监控和性能优化。 使用异步函数优化 I/O 密集型任务的性能，使用缓存策略和延迟加载。 使用 HTTPException 处理预期错误，并将其建模为特定的 HTTP 响应。 使用中间件处理意外错误、日志记录和错误监控。 使用 Pydantic 的 BaseModel 进行一致的输入/输出验证和响应模式。 性能优化 尽量减少阻塞 I/O 操作；对所有数据库调用和外部 API 请求使用异步操作。 使用 Redis 或内存存储等工具为静态和频繁访问的数据实现缓存。 使用 Pydantic 优化数据序列化和反序列化。 对大数据集和大量 API 响应使用延迟加载技术。 关键约定 依赖 FastAPI 的依赖注入系统来管理状态和共享资源。 优先考虑 API 性能指标（响应时间、延迟、吞吐量）。 限制路由中的阻塞操作： 优先使用异步和非阻塞流程。 使用专用的异步函数进行数据库和外部 API 操作。 清晰构建路由和依赖关系，以优化可读性和可维护性。 有关最佳实践，请参考 FastAPI 文档中的“数据模型”、“路径操作”和“中间件”部分。\n数据库 数据库使用mysql，数据库相关配置在settings目录的config.py文件进行操作。\n使用tortoseorm进行数据库操作\n要求必须使用一下的数据库迁移命令，不能够使用py自动执行迁移\n1 2 3 4 5 6 7 aerich init-db aerich migrate aerich upgrade aerich init -t backend.settings.config.TORTOISE_ORM --location ./backend/migrations ","date":"2026-01-26T00:00:00Z","permalink":"https://muioo.github.io/p/%E5%90%8E%E7%AB%AFfastapi%E5%BC%80%E5%8F%91%E8%A7%84%E5%88%99fastapi-%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91%E8%A7%84%E5%88%99/","title":"【后端FastApi开发规则】FastAPI 后端开发规则"},{"content":"git操作 已有仓库? 1 2 3 4 cd existing_git_repo git remote add origin https://gitee.com/.../... # 从仓库中拉取代码到本地 git pull origin master 更新本地仓库 1 git pull --rebase 仓库地址 [分支名/master] 推送到仓库 1 git push [仓库地址] [分支名/master] 要在两台电脑间协作开发（类似公司多设备办公），可以通过 「主分支 + 功能分支」 的 Git 分支策略来管理，既保证代码安全，又能顺畅同步。以下是具体步骤：\n一、分支策略设计（适合个人多设备协作） 参考公司常用的 「主分支 + 开发分支」 模式（简单易维护）：\nmain 分支：存放稳定的「已完成功能」代码（相当于生产环境代码）； dev 分支：日常开发的主分支（两台电脑都基于这个分支开发，同步代码）； （可选）临时功能分支：如果开发单个功能（比如 “订单支付模块”），可以从dev切出feature/pay分支，完成后合并回dev。 二、具体操作步骤（两台电脑分别执行） 步骤 1：初始化仓库（第一台电脑） 本地仓库关联远程仓库（已关联可跳过）：\n1 git remote add origin https://github.com/你的账号/OrderManagementSystem.git 创建并切换到dev开发分支：\n1 2 3 4 # 从main分支切出dev分支（首次创建） git checkout -b dev # 推送dev分支到远程仓库（让第二台电脑能拉取） git push -u origin dev 步骤 2：第一台电脑开发并提交代码 在dev分支修改代码后，提交并推送到远程：\n1 2 3 4 5 6 7 # 确保当前在dev分支 git checkout dev # 提交代码 git add . git commit -m \u0026#34;完成订单列表页面开发\u0026#34; # 推送到远程dev分支 git push origin dev 步骤 3：第二台电脑拉取代码并继续开发 克隆仓库（首次操作）：\n1 2 git clone https://github.com/你的账号/OrderManagementSystem.git cd OrderManagementSystem 拉取远程的dev分支并切换：\n1 2 3 4 # 拉取远程所有分支信息 git fetch origin # 切换到dev分支（自动关联远程dev） git checkout dev 开发后提交代码（和第一台电脑操作相同）:\n1 2 3 git add . git commit -m \u0026#34;优化订单筛选功能\u0026#34; git push origin dev 步骤 4：后续同步代码（两台电脑都要做） 每次开始开发前，先拉取远程dev分支的最新代码，避免冲突：\n1 2 git checkout dev git pull origin dev # 拉取远程最新代码到本地dev 三、冲突解决（如果出现代码冲突） 如果两台电脑修改了同一文件的同一部分，git pull会提示冲突，解决步骤：\n1.打开冲突文件，找到类似以下的标记：\n1 2 3 4 \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD 你本地的代码 ======= 远程的代码 1 2 3 4 5 6 # 远程分支名 2. 手动修改文件，保留需要的代码，删除冲突标记； 3. 提交解决后的代码： git add 冲突文件名 git commit -m \u0026#34;解决订单列表页面的代码冲突\u0026#34; git push origin dev 四、合并主分支代码（合并别人的代码） 场景 dev分支 有人修改了代码，并且测试功能无误 需要在master主分支合并\n1 2 3 4 5 6 7 # 切换到dev分支 拉取最新提交的代码 git checkout dev git pull # 切换到mster分支 git checkout master # 合并代码 git merge dev git配置代理 1 2 git config --global http.proxy http://127.0.0.1:7890 git config --global https.proxy https://127.0.0.1:7890 Git代理配置 ","date":"2026-01-23T00:00:00Z","permalink":"https://muioo.github.io/p/git%E5%91%BD%E4%BB%A4git-%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0%E5%8D%8F%E4%BD%9C-%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","title":"【Git命令】Git 操作笔记（协作+常用命令）"},{"content":"全局规则 1、在编写任何代码前，请先描述你的方案并等待批准，如果需求不明确，在编写任何代码之前必须提出澄清问题\n2、如果一项任务需要修改超过3个文件，请先停下来，将其分解成更小的任务\n3、编写代码后，列出可能出现的问题，并建议相应的测试用例来覆盖问题\n4、编码前先思考 不要假设。不要隐藏困惑。明确权衡。\n4.1、实施前：\n明确陈述你的假设。如果不确定，请询问。 如果存在多种解释，请呈现出来——不要默默选择。 如果存在更简单的方法，请说出来。必要时提出反对。 如果有不清楚的地方，停下来。指出困惑之处。询问。 2. 简洁优先 用最少的代码解决问题。不做推测性编码。\n不添加超出要求的功能。 不为一次性代码做抽象。 不添加未被要求的\u0026quot;灵活性\u0026quot;或\u0026quot;可配置性\u0026quot;。 不为不可能发生的情况做错误处理。 如果你写了 200 行而 50 行就能搞定，重写它。 问自己：\u0026ldquo;资深工程师会说这过于复杂吗？\u0026ldquo;如果是，简化它。\n5、精准修改 只碰必须碰的地方。只清理自己造成的混乱。\n5.1、编辑现有代码时：\n不要\u0026quot;改进\u0026quot;相邻的代码、注释或格式。 不要重构没坏的东西。 匹配现有风格，即使你自己会做得不同。 如果你注意到无关的死代码，提一下——但不要删除它。 当你的更改产生孤儿代码时：\n删除因你的更改而变得未使用的导入/变量/函数。 不要删除预先存在的死代码，除非被要求。 测试标准：每一行更改都应直接追溯到用户的请求。\n6、目标驱动执行 定义成功标准。循环直到验证通过。\n6.1、将任务转化为可验证的目标：\n\u0026ldquo;添加验证\u0026rdquo; → \u0026ldquo;为无效输入编写测试，然后让它们通过\u0026rdquo; \u0026ldquo;修复 bug\u0026rdquo; → \u0026ldquo;编写能复现它的测试，然后让它通过\u0026rdquo; \u0026ldquo;重构 X\u0026rdquo; → \u0026ldquo;确保测试在重构前后都能通过\u0026rdquo; 对于多步骤任务，简要陈述计划：\n[步骤] → 验证：[检查] [步骤] → 验证：[检查] [步骤] → 验证：[检查] 7、每次修改代码或着写代码请根据当前项目的功能、需求、代码结构等补充或者修改项目级AGENTS.md\n8、单文件不超过400行代码 如果超过必须拆分\n9、禁止硬编码，端口、url、配置 → 环境变量\n10、新建前必须搜索有没有类似功能，有就扩展，不要重复造轮子\n11、禁止静默异常，所有错误必须记录\n12、禁止向后兼容，旧代码直接删除\n13、尽可能要有详细的中文注释，至少每个函数都得有注释\n项目级AGENTS.md文档不要超过200行，文档需要有的是项目目标、代码风格/命名/格式以及目录结构。 必须要有详细的代码注释，不能为了压缩代码行数而缺少注释 ","date":"2026-01-21T00:00:00Z","permalink":"https://muioo.github.io/p/claude-code-%E5%85%A8%E5%B1%80%E8%A7%84%E5%88%99%E5%AE%9E%E7%94%A8%E7%9A%84%E5%85%A8%E5%B1%80%E8%A7%84%E5%88%99/","title":"【Claude Code 全局规则】实用的全局规则"},{"content":"阿里云服务器Ubutun上docker安装 安装依赖 1 apt install -y apt-transport-https ca-certificates curl software-properties-common 添加阿里云镜像 1 2 3 curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \u0026#34;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\u0026#34; | tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null 更新源 1 apt update 安装docker 1 apt install -y docker-ce docker-ce-cli containerd.io 验证安装成功 1 docker --version Windows安装 ","date":"2026-01-12T00:00:00Z","image":"https://muioo.github.io/p/docker-%E5%AE%89%E8%A3%85docker%E5%A4%9A%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%AE%89%E8%A3%85/docker-page_hu_443b9172da723ece.png","permalink":"https://muioo.github.io/p/docker-%E5%AE%89%E8%A3%85docker%E5%A4%9A%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%AE%89%E8%A3%85/","title":"【docker 安装】docker多系统的安装"}]