From 50c3d7ad735b9edf3eaaf9f47da1d93fd6d5c264 Mon Sep 17 00:00:00 2001 From: i-shm Date: Thu, 20 Aug 2026 02:41:23 +0000 Subject: deploy: cca9af916a79a76ed5408b765da3afeec6fbd8a4 --- search.xml | 659 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 361 insertions(+), 298 deletions(-) (limited to 'search.xml') diff --git a/search.xml b/search.xml index 7a1efd04..c9211e67 100644 --- a/search.xml +++ b/search.xml @@ -506,58 +506,6 @@ char sa_data[] socket address (variable-length data)

Technique - - F# 重载解析问题 - /2025/03/30/F-%E9%87%8D%E8%BD%BD%E8%A7%A3%E6%9E%90%E9%97%AE%E9%A2%98/ - 当在 F# 中使用一些 C# 类库的时候,可能会遇到类似如下的问题:

-
open System
open System.Numerics

let someFunction () =
let v: Vector<byte> = Vector(0uy)
let array: byte[] = [||]
let span: Span<byte> = Span(array)
v.CopyTo(span)
Compiler error
v.CopyTo(span)
----^^^^^^^^^^^^^^

/home/muqiu/stdin(99,5): error FS0041: A unique overload for method 'CopyTo' could not be determined based on type information prior to this program point. A type annotation may be needed.

Known type of argument: Span<byte>

Candidates:
- Vector.CopyTo(destination: Span<byte>) : unit
- Vector.CopyTo(destination: Span<byte>) : unit
-

编译器认为存在两个相同的 CopyTo 重载无法区分。但查阅文档发现,Vector<T> 的 CopyTo 方法实际上只有一个匹配的重载(接受 Span<T>),这似乎矛盾。

-

这是因为 F# 编译器处理泛型方法重载的方式:

-
    -
  • 泛型接口继承:Vector<T> 可能实现了多个接口,导致编译器看到两个签名相同的 CopyTo 方法(例如通过不同接口继承)。
  • -
  • 语言设计差异:F# 缺乏 C# 的 “most concrete” 重载选择机制。当多个重载来自不同继承路径时,F# 不会自动选择最具体的实现,需要显式指引。
  • -
-

仅我所知的一种解决方案是通过添加扩展方法显式指引编译器:

-
type Vector<'T> with
member inline this.CopyToByteSpan (span : Span<byte>) =
this.CopyTo(span) // 显式绑定具体重载

// 调用时使用新方法
v.CopyToByteSpan(span)
-

这种方法通过创建具体的类型路径,帮助编译器绕过复杂的重载解析逻辑。

-

学艺不精,不知道这是不是语言设计的差异,可能 F# 倾向于要求更明确的类型信息以避免意外行为?或许当泛型类型继承多个接口时,有没有可能出现在具体类型中不易察觉的隐式重载冲突?

-

Refs.

- -]]>
- - Technique - -
- - FDA 仿制药流程 - /2025/11/29/FDA-%E4%BB%BF%E5%88%B6%E8%8D%AF%E6%B5%81%E7%A8%8B/ - #药物研发

-

在FDA监管的药物研发、临床试验和上市流程中,当一个已上市药物(通常指创新药,通过新药申请NDA获得批准)出现一种更低廉的制药方式,但其活性成分的分子式保持完全相同时,这种情况本质上涉及仿制药(generic drug)的开发或工艺优化申请,而非从零开始的全新药物研发。简单来说,不需要重新进行完整的临床试验(即I-III期的大规模人体试验),但必须通过简化的监管路径证明新制药方式生产的药物与原药在质量、安全性和疗效上等效。

-

首先,理解核心前提:分子式完全相同意味着活性成分(active pharmaceutical ingredient, API)是相同的化学实体,例如从专利保护的原研药转向无专利保护的仿制药生产。这不同于生物制品(如单克隆抗体)的生物类似药,后者可能需要更多比较性临床试验。FDA将此类药物归类为“仿制药”,其审批路径是缩减新药申请(Abbreviated New Drug Application, ANDA),而非完整的NDA。ANDA的目的是避免重复证明已知的安全性和有效性数据,这些数据已在原药的NDA中确立[1]。

-

FDA的《仿制药用户费用法案》(GDUFA)和相关指南明确规定,对于相同分子式的药物,新制药方式(如改进的合成路线、晶型优化或更高效的提取工艺)只需证明“生物等效性”(bioequivalence),而非全面临床试验。生物等效性研究通常包括:1)体外溶出测试(in vitro dissolution),评估药物在模拟生理条件下的释放速率;2)人体药代动力学研究(pharmacokinetic studies),在健康志愿者中比较新药与原药的吸收、分布、代谢和排泄曲线(如AUC和Cmax参数的90%置信区间在80%-125%内)。这些研究规模小,通常只需24-36名受试者,持续数周,而非数年[2]。如果新工艺导致杂质谱或稳定性变化,FDA可能要求额外的数据,如加速稳定性测试或毒性评估,但仍无需重复疗效试验,除非有证据显示潜在差异(如新杂质超过许可阈值)[3]。

-

在实际操作中,这一路径大大降低了成本和时间:ANDA审批平均需10-15个月,费用远低于NDA的数亿美元和数年周期。新制药方式的低廉性往往源于规模化生产或工艺创新,但FDA要求申请者提交化学、制造和控制(CMC)信息,证明新工艺符合良好生产规范(cGMP)。例如,阿司匹林或扑热息痛等经典药物,其多种仿制药版本均通过此路径上市,而无需重新验证其止痛或抗炎疗效[4]。然而,如果新方式涉及重大变更(如从化学合成转为生物合成,尽管分子式相同),FDA可能视其为“混合型”申请,需要桥接研究来确认等效性。

-

如果生物等效性未能证明(例如,新工艺导致生物利用度差异>20%),申请将被拒,或需额外桥接试验,这可能增加延误和成本。不过上市后监测(post-marketing surveillance)仍适用,包括不良事件报告(FAERS系统),以捕捉任何罕见差异。

-
-
-
    -
  1. U.S. Food and Drug Administration. (2023). Frequently Asked Questions about Generic Drugs. Retrieved from https://www.fda.gov/drugs/generic-drugs/frequently-asked-questions-about-generic-drugs ↩︎

    -
  2. -
  3. U.S. Food and Drug Administration. (2019). Bioequivalence Studies with Pharmacokinetic Endpoints for Drugs Submitted Under an ANDA. Guidance for Industry. Retrieved from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/bioequivalence-studies-pharmacokinetic-endpoints-drugs-submitted-under-abbreviated-new-drug ↩︎

    -
  4. -
  5. U.S. Food and Drug Administration. (2020). Changes to an Approved NDA or ANDA. Guidance for Industry. Retrieved from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/changes-approved-nda-or-anda ↩︎

    -
  6. -
  7. U.S. Food and Drug Administration. (2023). Approved Drug Products with Therapeutic Equivalence Evaluations (Orange Book). Retrieved from https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book ↩︎

    -
  8. -
-
-]]>
- - Medicine - -
Dealing with complex dependency injection in FSharp /2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/ @@ -652,6 +600,58 @@ char sa_data[] socket address (variable-length data)

Archive
+ + F# 重载解析问题 + /2025/03/30/F-%E9%87%8D%E8%BD%BD%E8%A7%A3%E6%9E%90%E9%97%AE%E9%A2%98/ + 当在 F# 中使用一些 C# 类库的时候,可能会遇到类似如下的问题:

+
open System
open System.Numerics

let someFunction () =
let v: Vector<byte> = Vector(0uy)
let array: byte[] = [||]
let span: Span<byte> = Span(array)
v.CopyTo(span)
Compiler error
v.CopyTo(span)
----^^^^^^^^^^^^^^

/home/muqiu/stdin(99,5): error FS0041: A unique overload for method 'CopyTo' could not be determined based on type information prior to this program point. A type annotation may be needed.

Known type of argument: Span<byte>

Candidates:
- Vector.CopyTo(destination: Span<byte>) : unit
- Vector.CopyTo(destination: Span<byte>) : unit
+

编译器认为存在两个相同的 CopyTo 重载无法区分。但查阅文档发现,Vector<T> 的 CopyTo 方法实际上只有一个匹配的重载(接受 Span<T>),这似乎矛盾。

+

这是因为 F# 编译器处理泛型方法重载的方式:

+
    +
  • 泛型接口继承:Vector<T> 可能实现了多个接口,导致编译器看到两个签名相同的 CopyTo 方法(例如通过不同接口继承)。
  • +
  • 语言设计差异:F# 缺乏 C# 的 “most concrete” 重载选择机制。当多个重载来自不同继承路径时,F# 不会自动选择最具体的实现,需要显式指引。
  • +
+

仅我所知的一种解决方案是通过添加扩展方法显式指引编译器:

+
type Vector<'T> with
member inline this.CopyToByteSpan (span : Span<byte>) =
this.CopyTo(span) // 显式绑定具体重载

// 调用时使用新方法
v.CopyToByteSpan(span)
+

这种方法通过创建具体的类型路径,帮助编译器绕过复杂的重载解析逻辑。

+

学艺不精,不知道这是不是语言设计的差异,可能 F# 倾向于要求更明确的类型信息以避免意外行为?或许当泛型类型继承多个接口时,有没有可能出现在具体类型中不易察觉的隐式重载冲突?

+

Refs.

+ +]]>
+ + Technique + +
+ + FDA 仿制药流程 + /2025/11/29/FDA-%E4%BB%BF%E5%88%B6%E8%8D%AF%E6%B5%81%E7%A8%8B/ + #药物研发

+

在FDA监管的药物研发、临床试验和上市流程中,当一个已上市药物(通常指创新药,通过新药申请NDA获得批准)出现一种更低廉的制药方式,但其活性成分的分子式保持完全相同时,这种情况本质上涉及仿制药(generic drug)的开发或工艺优化申请,而非从零开始的全新药物研发。简单来说,不需要重新进行完整的临床试验(即I-III期的大规模人体试验),但必须通过简化的监管路径证明新制药方式生产的药物与原药在质量、安全性和疗效上等效。

+

首先,理解核心前提:分子式完全相同意味着活性成分(active pharmaceutical ingredient, API)是相同的化学实体,例如从专利保护的原研药转向无专利保护的仿制药生产。这不同于生物制品(如单克隆抗体)的生物类似药,后者可能需要更多比较性临床试验。FDA将此类药物归类为“仿制药”,其审批路径是缩减新药申请(Abbreviated New Drug Application, ANDA),而非完整的NDA。ANDA的目的是避免重复证明已知的安全性和有效性数据,这些数据已在原药的NDA中确立[1]。

+

FDA的《仿制药用户费用法案》(GDUFA)和相关指南明确规定,对于相同分子式的药物,新制药方式(如改进的合成路线、晶型优化或更高效的提取工艺)只需证明“生物等效性”(bioequivalence),而非全面临床试验。生物等效性研究通常包括:1)体外溶出测试(in vitro dissolution),评估药物在模拟生理条件下的释放速率;2)人体药代动力学研究(pharmacokinetic studies),在健康志愿者中比较新药与原药的吸收、分布、代谢和排泄曲线(如AUC和Cmax参数的90%置信区间在80%-125%内)。这些研究规模小,通常只需24-36名受试者,持续数周,而非数年[2]。如果新工艺导致杂质谱或稳定性变化,FDA可能要求额外的数据,如加速稳定性测试或毒性评估,但仍无需重复疗效试验,除非有证据显示潜在差异(如新杂质超过许可阈值)[3]。

+

在实际操作中,这一路径大大降低了成本和时间:ANDA审批平均需10-15个月,费用远低于NDA的数亿美元和数年周期。新制药方式的低廉性往往源于规模化生产或工艺创新,但FDA要求申请者提交化学、制造和控制(CMC)信息,证明新工艺符合良好生产规范(cGMP)。例如,阿司匹林或扑热息痛等经典药物,其多种仿制药版本均通过此路径上市,而无需重新验证其止痛或抗炎疗效[4]。然而,如果新方式涉及重大变更(如从化学合成转为生物合成,尽管分子式相同),FDA可能视其为“混合型”申请,需要桥接研究来确认等效性。

+

如果生物等效性未能证明(例如,新工艺导致生物利用度差异>20%),申请将被拒,或需额外桥接试验,这可能增加延误和成本。不过上市后监测(post-marketing surveillance)仍适用,包括不良事件报告(FAERS系统),以捕捉任何罕见差异。

+
+
+
    +
  1. U.S. Food and Drug Administration. (2023). Frequently Asked Questions about Generic Drugs. Retrieved from https://www.fda.gov/drugs/generic-drugs/frequently-asked-questions-about-generic-drugs ↩︎

    +
  2. +
  3. U.S. Food and Drug Administration. (2019). Bioequivalence Studies with Pharmacokinetic Endpoints for Drugs Submitted Under an ANDA. Guidance for Industry. Retrieved from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/bioequivalence-studies-pharmacokinetic-endpoints-drugs-submitted-under-abbreviated-new-drug ↩︎

    +
  4. +
  5. U.S. Food and Drug Administration. (2020). Changes to an Approved NDA or ANDA. Guidance for Industry. Retrieved from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/changes-approved-nda-or-anda ↩︎

    +
  6. +
  7. U.S. Food and Drug Administration. (2023). Approved Drug Products with Therapeutic Equivalence Evaluations (Orange Book). Retrieved from https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book ↩︎

    +
  8. +
+
+]]>
+ + Medicine + +
FHIR 记录患者的用药记录 /2025/12/29/FHIR-%E8%AE%B0%E5%BD%95%E6%82%A3%E8%80%85%E7%9A%84%E7%94%A8%E8%8D%AF%E8%AE%B0%E5%BD%95/ @@ -837,21 +837,6 @@ Given the impracticality of prolonged hospitalizations and advancing technology Archive - - 什么是 G⁺ - /2025/10/19/Gram-stain/ - G⁺(Gram‑positive) 是细菌在 Gram 染色(Gram stain)技术下呈现的染色特征。Gram 染色是一种经典的显微镜染色方法,依据细菌细胞壁结构的差异,将细菌分为两大类:Gram‑positive(G⁺) 和 Gram‑negative(G⁻)。在染色程序中,细菌首先被结晶紫染料染色,随后加入碘液形成复合物,接着用酒精或丙酮脱色,最后用沙黄或复红复染。若细菌的细胞壁能够 保留结晶紫‑碘复合物,则在显微镜下呈现紫蓝色,这类细菌即被归类为 Gram‑positive(G⁺);若细胞壁结构不保留复合物而被脱色,随后被沙黄染成红色,则为 Gram‑negative(G⁻)。

-

G⁺ 细菌的细胞壁外层主要由 厚度约 20–80 nm 的肽聚糖(peptidoglycan) 组成,这层结构密集且富含交联的糖肽链,能够在脱色步骤中形成物理屏障,阻止酒精/丙酮的渗透,从而保留结晶紫‑碘复合物。

-

与 G⁻ 细菌不同,G⁺ 细菌没有外膜(outer membrane)和脂多糖(LPS)层。外膜的缺失使得脱色剂难以进入细胞内部,进一步加强了紫色的保留。

-

某些 G⁺ 细菌在细胞壁上表达的酶(如酸性磷酸酶)可与染料形成更为稳定的复合物,进一步提升染色的持久性。

-

在呼吸道痰涂片中出现 “有荚膜的双球菌(capsulated diplococci)”,最典型的病原体是 肺炎链球菌(Streptococcus pneumoniae)。它是一种 革兰氏阳性(G⁺)、α‑hemolytic、嗜血清素 的双球菌,表面包裹有厚厚的多糖荚膜,正是该荚膜赋予它在显微镜下呈现明显的荧光或“胶状”外观。

-

在痰涂片或血培养的显微镜检查中,观察到 G⁺ 双球菌提示肺炎链球菌感染的可能性较大,帮助临床医生在培养结果出来前就能启动经验性抗生素治疗(如 β‑lactam 类药物)。

-

肺炎链球菌的荚膜多型性(> 90 种血清型)是疫苗设计的核心依据,同时也影响抗体介导的吞噬作用。了解其 G⁺ 特性有助于解释为何某些血清型(如 3 型)在免疫逃逸方面更具优势。

-]]>
- - Medicine - -
G-Machine /2024/01/23/G-Machine/ @@ -878,6 +863,21 @@ Given the impracticality of prolonged hospitalizations and advancing technology Technique + + 什么是 G⁺ + /2025/10/19/Gram-stain/ + G⁺(Gram‑positive) 是细菌在 Gram 染色(Gram stain)技术下呈现的染色特征。Gram 染色是一种经典的显微镜染色方法,依据细菌细胞壁结构的差异,将细菌分为两大类:Gram‑positive(G⁺) 和 Gram‑negative(G⁻)。在染色程序中,细菌首先被结晶紫染料染色,随后加入碘液形成复合物,接着用酒精或丙酮脱色,最后用沙黄或复红复染。若细菌的细胞壁能够 保留结晶紫‑碘复合物,则在显微镜下呈现紫蓝色,这类细菌即被归类为 Gram‑positive(G⁺);若细胞壁结构不保留复合物而被脱色,随后被沙黄染成红色,则为 Gram‑negative(G⁻)。

+

G⁺ 细菌的细胞壁外层主要由 厚度约 20–80 nm 的肽聚糖(peptidoglycan) 组成,这层结构密集且富含交联的糖肽链,能够在脱色步骤中形成物理屏障,阻止酒精/丙酮的渗透,从而保留结晶紫‑碘复合物。

+

与 G⁻ 细菌不同,G⁺ 细菌没有外膜(outer membrane)和脂多糖(LPS)层。外膜的缺失使得脱色剂难以进入细胞内部,进一步加强了紫色的保留。

+

某些 G⁺ 细菌在细胞壁上表达的酶(如酸性磷酸酶)可与染料形成更为稳定的复合物,进一步提升染色的持久性。

+

在呼吸道痰涂片中出现 “有荚膜的双球菌(capsulated diplococci)”,最典型的病原体是 肺炎链球菌(Streptococcus pneumoniae)。它是一种 革兰氏阳性(G⁺)、α‑hemolytic、嗜血清素 的双球菌,表面包裹有厚厚的多糖荚膜,正是该荚膜赋予它在显微镜下呈现明显的荧光或“胶状”外观。

+

在痰涂片或血培养的显微镜检查中,观察到 G⁺ 双球菌提示肺炎链球菌感染的可能性较大,帮助临床医生在培养结果出来前就能启动经验性抗生素治疗(如 β‑lactam 类药物)。

+

肺炎链球菌的荚膜多型性(> 90 种血清型)是疫苗设计的核心依据,同时也影响抗体介导的吞噬作用。了解其 G⁺ 特性有助于解释为何某些血清型(如 3 型)在免疫逃逸方面更具优势。

+]]>
+ + Medicine + +
多人协作系统中的实现策略 (CRDT,锁等实现方案) /2025/05/08/Multiplayer-Collaborative-Systems-tips/ @@ -1096,6 +1096,40 @@ await prisma.$transaction(async tx => {

正确顺序:

location = /api/auth/me {
# 精确匹配,业务身份接口
rewrite ^/api(.*)$ $1 break;
proxy_pass http://host.docker.internal:<backend>;
}

location ^~ /api/auth/ {
# 认证框架路由,保留前缀
proxy_pass http://host.docker.internal:<auth-port>;
}

location ^~ /api/ {
# 其余 API,裁掉 /api
rewrite ^/api(.*)$ $1 break;
proxy_pass http://host.docker.internal:<backend>;
}

location / {
# 前端
proxy_pass http://host.docker.internal:<frontend>;
}

这里的顺序很重要。Nginx 匹配规则是:精确匹配 = 优先,然后是前缀匹配 ^~,然后是正则匹配,最后是普通前缀匹配。^~ 表示一旦匹配成功就不再检查正则。

+]]> + + Technique + +
+ + OCaml Core.Int.pow 的实现 + /2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/ + Core内部直接引用的Base的实现, Base的实现在 src/int_math.ml 中:

+
let int_pow base exponent =
if exponent < 0 then negative_exponent ();
if abs base > 1
&& (exponent > 63
|| abs base > Pow_overflow_bounds.int_positive_overflow_bounds.(exponent))
then overflow ();
int_math_int_pow base exponent
;;
+

其中 int_math_int_pow() 由 C 实现:

+
external int_math_int_pow : int -> int -> int = "Base_int_math_int_pow_stub" [@@noalloc]
+

其实现在 src/int_math_stubs.c 中:

+
static int64_t int_pow(int64_t base, int64_t exponent) {
int64_t ret = 1;
int64_t mul[4];
mul[0] = 1;
mul[1] = base;
mul[3] = 1;

while (exponent != 0) {
mul[1] *= mul[3];
mul[2] = mul[1] * mul[1];
mul[3] = mul[2] * mul[1];
ret *= mul[exponent & 3];
exponent >>= 2;
}

return ret;
}
+

这是一个四分快速幂的实现,它是二分快速幂的一种变种。二分快速幂将指数分为两部分,然后递归地计算每一部分的结果。
+而四分快速幂将指数分为四部分,然后递归地计算每一部分的结果。
+这里通过将指数右移2位(相当于除以4)和使用位与操作来实现,进一步减少了乘法次数。

+

主要步骤:

+
    +
  • 初始化返回值ret为1,和一个包含4个元素的数组mul, mul[0]和mul[3]被初始化为1, mul[1]被初始化为基数
  • +
  • 当指数不为0时,执行循环, 在每次循环中,首先更新mul数组的值 +
    +

    mul[1]是基数和mul[3]的乘积,mul[2]是mul[1]的平方,mul[3]是mul[2]和基数的乘积

    +
    +
  • +
  • 然后,将ret乘以mul数组中的一个元素, 这个元素的索引是指数和3的位与运算的结果 +
    +

    这样做的目的是为了选择正确的乘数,因为指数被分解为4的倍数

    +
    +
  • +
  • 最后,将指数右移2位,相当于将指数除以4
  • +
  • 当指数变为0时,循环结束,返回ret
  • +
+

这个实现的优点是它可以在对数时间内计算出幂运算,而且每次循环只需要4次乘法。这比标准的二分快速幂算法需要的乘法次数更少。

]]>
Technique @@ -1159,40 +1193,6 @@ await prisma.$transaction(async tx => {
  • RFSM 2.0: A toolset for describing and simulating StateChart-like state diagrams.
  • Zbg 2.0: zbg (short for Zero Bullshit Git) is a CLI tool for using git efficiently.
  • -]]> - - Technique - -
    - - OCaml Core.Int.pow 的实现 - /2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/ - Core内部直接引用的Base的实现, Base的实现在 src/int_math.ml 中:

    -
    let int_pow base exponent =
    if exponent < 0 then negative_exponent ();
    if abs base > 1
    && (exponent > 63
    || abs base > Pow_overflow_bounds.int_positive_overflow_bounds.(exponent))
    then overflow ();
    int_math_int_pow base exponent
    ;;
    -

    其中 int_math_int_pow() 由 C 实现:

    -
    external int_math_int_pow : int -> int -> int = "Base_int_math_int_pow_stub" [@@noalloc]
    -

    其实现在 src/int_math_stubs.c 中:

    -
    static int64_t int_pow(int64_t base, int64_t exponent) {
    int64_t ret = 1;
    int64_t mul[4];
    mul[0] = 1;
    mul[1] = base;
    mul[3] = 1;

    while (exponent != 0) {
    mul[1] *= mul[3];
    mul[2] = mul[1] * mul[1];
    mul[3] = mul[2] * mul[1];
    ret *= mul[exponent & 3];
    exponent >>= 2;
    }

    return ret;
    }
    -

    这是一个四分快速幂的实现,它是二分快速幂的一种变种。二分快速幂将指数分为两部分,然后递归地计算每一部分的结果。
    -而四分快速幂将指数分为四部分,然后递归地计算每一部分的结果。
    -这里通过将指数右移2位(相当于除以4)和使用位与操作来实现,进一步减少了乘法次数。

    -

    主要步骤:

    -
      -
    • 初始化返回值ret为1,和一个包含4个元素的数组mul, mul[0]和mul[3]被初始化为1, mul[1]被初始化为基数
    • -
    • 当指数不为0时,执行循环, 在每次循环中,首先更新mul数组的值 -
      -

      mul[1]是基数和mul[3]的乘积,mul[2]是mul[1]的平方,mul[3]是mul[2]和基数的乘积

      -
      -
    • -
    • 然后,将ret乘以mul数组中的一个元素, 这个元素的索引是指数和3的位与运算的结果 -
      -

      这样做的目的是为了选择正确的乘数,因为指数被分解为4的倍数

      -
      -
    • -
    • 最后,将指数右移2位,相当于将指数除以4
    • -
    • 当指数变为0时,循环结束,返回ret
    • -
    -

    这个实现的优点是它可以在对数时间内计算出幂运算,而且每次循环只需要4次乘法。这比标准的二分快速幂算法需要的乘法次数更少。

    ]]>
    Technique @@ -2042,28 +2042,6 @@ await prisma.$transaction(async tx => { Technique
    - - Radiographic Absence of the Left Humeral Head - /2023/10/25/Radiographic-Absence-of-the-Left-Humeral-Head/ - A 67-year-old man with thoracolumbar scoliosis, poor mobility, and history of frequent falls presented to the emergency department with 2 months of left shoulder pain, stiffness and reduced range of motion, and numbness and paresthesias in his left upper extremity.

    -

    Ten years prior to presentation, he underwent surgical decompression for syringomyelia.

    -

    A magnetic resonance imaging (MRI) scan of his cervical and thoracic spine performed 2 years prior to presentation revealed a recurrent syrinx extending from Cl C1 to T11, which was not resected because it did not cause symptoms at that time.

    -

    On physical examination, he had mild tenderness to palpation and reduced range of motion of the left shoulder with abduction and flexion limited to 120° (normal range of motion, 180°).

    -

    The left scapular muscles were atrophic, and pain and temperature sensation were reduced in his proximal left arm, and dorsal aspect of his left shoulder.

    -

    His complete blood cell count, serum glucose levels, C-reactive protein levels, and erythrocyte sedimentation rate were normal.

    -

    Results of tests for rheumatoid factor and antinuclear antibody were negative.

    -

    Left shoulder radiograph showed complete absence of the left humeral head and a well-demarcated smooth osseous margin of the proximal humerus with associated soft tissue swelling and periarticular calcification.

    -

    A chest radiograph taken 2 years prior revealed a normal left shoulder joint. The patient was hospitalized for further evaluation and treatment.

    - -

    这里提到了一种罕见的进展性疾病, 叫Charcot肩, 是一种神经性关节病变,特点是关节迅速被破坏和相关软组织肿胀。患者通常表现为肩部逐渐增大的肿胀,可能伴有疼痛或无痛,肩部无力和活动范围减少。
    -大约80%的Charcot肩患者有脊髓空洞症, 表现为脊髓中的液体充满腔体

    -]]>
    - - Medicine - -
    Prisma 关系型数据库的 Self-relations /2025/02/18/Prisma-%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84-Self-relations/ @@ -2132,6 +2110,28 @@ A relation table (also sometimes called a JOIN, link or pi Technique + + Radiographic Absence of the Left Humeral Head + /2023/10/25/Radiographic-Absence-of-the-Left-Humeral-Head/ + A 67-year-old man with thoracolumbar scoliosis, poor mobility, and history of frequent falls presented to the emergency department with 2 months of left shoulder pain, stiffness and reduced range of motion, and numbness and paresthesias in his left upper extremity.

    +

    Ten years prior to presentation, he underwent surgical decompression for syringomyelia.

    +

    A magnetic resonance imaging (MRI) scan of his cervical and thoracic spine performed 2 years prior to presentation revealed a recurrent syrinx extending from Cl C1 to T11, which was not resected because it did not cause symptoms at that time.

    +

    On physical examination, he had mild tenderness to palpation and reduced range of motion of the left shoulder with abduction and flexion limited to 120° (normal range of motion, 180°).

    +

    The left scapular muscles were atrophic, and pain and temperature sensation were reduced in his proximal left arm, and dorsal aspect of his left shoulder.

    +

    His complete blood cell count, serum glucose levels, C-reactive protein levels, and erythrocyte sedimentation rate were normal.

    +

    Results of tests for rheumatoid factor and antinuclear antibody were negative.

    +

    Left shoulder radiograph showed complete absence of the left humeral head and a well-demarcated smooth osseous margin of the proximal humerus with associated soft tissue swelling and periarticular calcification.

    +

    A chest radiograph taken 2 years prior revealed a normal left shoulder joint. The patient was hospitalized for further evaluation and treatment.

    + +

    这里提到了一种罕见的进展性疾病, 叫Charcot肩, 是一种神经性关节病变,特点是关节迅速被破坏和相关软组织肿胀。患者通常表现为肩部逐渐增大的肿胀,可能伴有疼痛或无痛,肩部无力和活动范围减少。
    +大约80%的Charcot肩患者有脊髓空洞症, 表现为脊髓中的液体充满腔体

    +]]>
    + + Medicine + +
    Repository pattern in Typescript /2025/02/19/Repository-pattern-in-Typescript/ @@ -2274,22 +2274,6 @@ A relation table (also sometimes called a JOIN, link or pi Technique - - Rust 闭包 lifetime may not live long enough 问题 - /2023/05/24/Rust-%E9%97%AD%E5%8C%85-lifetime-may-not-live-long-enough-%E9%97%AE%E9%A2%98/ - 代码:

    -
    ...
    fn handlers(self) -> crate::server::request::Handlers {
    vec![(
    "/tree",
    routing::get(move || async {
    (
    StatusCode::OK,
    Json(json!(self.clone().tree(self.clone().root))),
    )
    }),
    )]
    }
    ...
    -

    编译错误:

    -
    error: lifetime may not live long enough
    --> src/storage/filesystem/mod.rs:45:34
    |
    45 | routing::get(move || async {
    | __________________________-------_^
    | | | |
    | | | return type of closure `{async block@src/storage/filesystem/mod.rs:45:34: 50:14}` contains a lifetime `'2`
    | | lifetime `'1` represents this closure's body
    46 | | (
    47 | | StatusCode::OK,
    48 | | Json(json!(self.clone().tree(self.clone().root))),
    49 | | )
    50 | | }),
    | |_____________^ returning this value requires that `'1` must outlive `'2`
    |
    = note: closure implements `Fn`, so references to captured variables can't escape the closure
    -

    这是因为 handlers 里面的闭包捕获了一个引用,并且尝试返回一个包含该引用的值导致的。

    -

    细说就是:闭包内部使用了 self.clone() 来获取一个新的实例,然后在异步块中返回一个 JSON 对象,这个 JSON 对象依赖于 self.tree() 的结果。因为闭包捕获了 self 的引用,所以它必须保证 self 在闭包执行完毕后仍然有效。

    -

    解决这个问题的思路是:确保闭包中的所有引用都在闭包执行完毕之前就不再被使用。
    -就是说,要将闭包的作用域限制在一个更短的生命周期内,或者使用其他方式来避免闭包捕获长期存在的引用:

    -

    ...
    fn handlers(self) -> crate::server::request::Handlers {
    let tree = json!(self.clone().tree(self.clone().root));
    vec![(
    "/tree",
    routing::get(move || async { (StatusCode::OK, Json(tree)) }),
    )]
    }
    ...
    ]]>
    - - Technique - -
    Rust 虚表布局规则介绍 /2023/05/01/Rust-%E8%99%9A%E8%A1%A8%E5%B8%83%E5%B1%80%E8%A7%84%E5%88%99%E4%BB%8B%E7%BB%8D/ @@ -2336,6 +2320,22 @@ A relation table (also sometimes called a JOIN, link or pi Technique + + Rust 闭包 lifetime may not live long enough 问题 + /2023/05/24/Rust-%E9%97%AD%E5%8C%85-lifetime-may-not-live-long-enough-%E9%97%AE%E9%A2%98/ + 代码:

    +
    ...
    fn handlers(self) -> crate::server::request::Handlers {
    vec![(
    "/tree",
    routing::get(move || async {
    (
    StatusCode::OK,
    Json(json!(self.clone().tree(self.clone().root))),
    )
    }),
    )]
    }
    ...
    +

    编译错误:

    +
    error: lifetime may not live long enough
    --> src/storage/filesystem/mod.rs:45:34
    |
    45 | routing::get(move || async {
    | __________________________-------_^
    | | | |
    | | | return type of closure `{async block@src/storage/filesystem/mod.rs:45:34: 50:14}` contains a lifetime `'2`
    | | lifetime `'1` represents this closure's body
    46 | | (
    47 | | StatusCode::OK,
    48 | | Json(json!(self.clone().tree(self.clone().root))),
    49 | | )
    50 | | }),
    | |_____________^ returning this value requires that `'1` must outlive `'2`
    |
    = note: closure implements `Fn`, so references to captured variables can't escape the closure
    +

    这是因为 handlers 里面的闭包捕获了一个引用,并且尝试返回一个包含该引用的值导致的。

    +

    细说就是:闭包内部使用了 self.clone() 来获取一个新的实例,然后在异步块中返回一个 JSON 对象,这个 JSON 对象依赖于 self.tree() 的结果。因为闭包捕获了 self 的引用,所以它必须保证 self 在闭包执行完毕后仍然有效。

    +

    解决这个问题的思路是:确保闭包中的所有引用都在闭包执行完毕之前就不再被使用。
    +就是说,要将闭包的作用域限制在一个更短的生命周期内,或者使用其他方式来避免闭包捕获长期存在的引用:

    +

    ...
    fn handlers(self) -> crate::server::request::Handlers {
    let tree = json!(self.clone().tree(self.clone().root));
    vec![(
    "/tree",
    routing::get(move || async { (StatusCode::OK, Json(tree)) }),
    )]
    }
    ...
    ]]>
    + + Technique + +
    Scala 3 (Capture Checking) /2025/03/25/Scala-3-Capture-Checking/ @@ -3229,90 +3229,38 @@ A relation table (also sometimes called a JOIN, link or pi

    4. 调节脑-肠轴功能的药物(精神类药物)

    对于伴有明显焦虑、抑郁或常规治疗无效的顽固性腹痛患者,医生可能会考虑使用低剂量的抗抑郁药。

    -
      -
    • 三环类抗抑郁药 (TCAs)(如阿米替林): 低剂量使用可以有效缓解IBS的慢性腹痛,其抗胆碱能作用也有助于控制腹泻。
    • -
    • 选择性5-羟色胺再摄取抑制剂 (SSRIs)(如氟西汀、帕罗西汀): 可用于伴有便秘和焦虑的IBS患者。
    • -
    -
      -
    1. 益生菌 (Probiotics)
    2. -
    -

    补充特定的益生菌菌株(如某些双歧杆菌和乳酸杆菌)有助于恢复肠道微生态平衡,改善腹胀和总体症状。不同菌株效果各异,需根据产品说明和医生建议选用。

    -]]> - - Medicine - -
    - - libdecor-gtk-WARNING Failed to initialize GTK in VSCode on Wayland - /2025/03/30/libdecor-gtk-WARNING-Failed-to-initialize-GTK/ - 在 Wayland 上的 VSCode 中使用 libdecor-gtk 时,可能会遇到以下警告:

    -
    libdecor-gtk-WARNING: Failed to initialize GTK
    -

    这是因为 VSCode 默认使用 X11,可以使用以下参数启动 VSCode:

    -
    code --enable-features=UseOzonePlatform --ozone-platform=wayland
    -]]>
    - - Technique - -
    - - Linux 下 AMD 显卡屏幕画面闪烁问题修复方案 - /2025/04/20/linux-amd-screen-boom/ - 不知道我们所说的闪烁是不是一个东西,在我的笔记本上,闪烁是指偶发的屏幕中出现部分彩色雪花。

    -

    我使用 Pop!_OS,此发行版使用 EFI 启动,故而可以在 /boot/efi/loader/loader.conf 中加上:

    -
    options quiet splash amdgpu.dcdebugmask=0x10 amdgpu.sg_display=0
    -

    对于其他使用 EFI 或 GRUB 启动的发行版也可以添加类似的参数尝试尝试。

    -]]>
    - - Technique - -
    - - NestJS 中 enableImplicitConversion 与 @Transform 的冲突 - /2025/08/13/nestjs-enableImplicitConversion-and-transform/ - 在 NestJS 生态中,class-validator 和 class-transformer 这两个库提供了以声明式的方式对 DTO 进行验证和转换。然而在处理布尔值时,如果在全局验证管道或仅仅是在局部同时开启了 enableImplicitConversion,可能会引入一个极其隐蔽且违反直觉的 Bug:前端传过来的布尔值恒为 true。

    -

    一个简单的筛选功能

    -

    假设正在开发一个电子商务平台的 API,需要实现一个产品列表的筛选功能。希望能够根据产品是否有库存 (hasStock)、是否为特色产品 (isFeatured) 等布尔条件进行筛选。

    -

    前端发出的请求 URL 可能如下所示: /products?filter[hasStock]=true&filter[isFeatured]=false

    -

    在 NestJS 后端,首先会在 main.ts 中配置一个全局的 ValidationPipe,以自动处理 DTO 的验证和转换。为了方便,通常会启用 enableImplicitConversion,期望它能自动将 URL 查询参数中的字符串(如 "123", "true")转换为 DTO 中定义的类型(number, boolean):

    -
    // main.ts
    import { ValidationPipe } from '@nestjs/common';
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';

    async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    app.useGlobalPipes(
    new ValidationPipe({
    transform: true, // 启用转换
    whitelist: true,
    forbidNonWhitelisted: true,
    transformOptions: {
    // 启用基于 TypeScript 类型的隐式转换
    enableImplicitConversion: true,
    },
    }),
    );

    // ... 其他配置
    await app.listen(3000);
    }
    bootstrap();
    -

    接着,定义一个 ProductFilterDto 来接收这些筛选条件。

    -

    一个看似正确的 DTO 定义:

    -
    // product-filter.dto.ts
    import { IsBoolean, IsOptional } from 'class-validator';

    export class ProductFilterDto {
    @IsOptional()
    @IsBoolean()
    hasStock?: boolean;

    @IsOptional()
    @IsBoolean()
    isFeatured?: boolean;
    }
    -

    在控制器中使用这个 DTO:

    -
    // products.controller.ts
    @Controller('products')
    export class ProductsController {
    @Get()
    find(@Query('filter') filter: ProductFilterDto) {
    // 期望 filter.isFeatured 的值为 boolean false
    console.log(filter);
    // ... 业务逻辑
    }
    }
    -

    当请求 .../products?filter[isFeatured]=false 到达时,本来期望在 find 方法中得到的 filter.isFeatured 的值是布尔类型的 false。然而,控制台输出的结果却令人意外:{ isFeatured: true }

    -

    问题剖析

    -

    这个问题的根源在于 class-transformer 内部的转换执行顺序,以及 JavaScript 中 Boolean 函数的类型转换行为。

    -

    所有通过 URL 查询参数传递的值,其本质都是字符串。当 NestJS 接收到请求时,filter.isFeatured 的原始值是字符串 "false"。

    -

    ValidationPipe 启动 class-transformer 的转换流程。由于在全局管道中设置了 enableImplicitConversion: true,转换器会首先检查 DTO 属性的 TypeScript 类型。

    -
      -
    1. 隐式转换优先执行:class-transformer 看到 ProductFilterDto 中的 isFeatured 属性被声明为 boolean 类型。
    2. -
    3. 错误的类型转换:它立即尝试将字符串 "false" 转换为布尔值。这个转换等同于执行 Boolean("false")。在 JavaScript 中,任何非空字符串(包括 "false")通过 Boolean() 构造函数转换后都会得到 true。
    4. -
    5. 结果覆盖:这个错误的 true 值被作为该属性的转换结果。
    6. -
    -

    此时,即使尝试添加一个自定义的 @Transform 装饰器来手动处理这个问题,也为时已晚。

    -

    例如,定义一个 booleanTransformer:

    -
    // boolean-transformer.ts
    export const booleanTransformer = ({ value }: { value: any }) => {
    if (typeof value === 'string') {
    return value === 'true';
    }
    return value;
    };
    -

    然后更新 dto:

    -
    // product-filter.dto.ts (错误的尝试)
    import { Transform } from 'class-transformer';
    import { IsBoolean, IsOptional } from 'class-validator';
    import { booleanTransformer } from './boolean-transformer';

    export class ProductFilterDto {
    // ...
    @IsOptional()
    @Transform(booleanTransformer) // 添加自定义转换
    @IsBoolean()
    isFeatured?: boolean;
    }
    -

    流程会变成这样:

    -
      -
    1. 隐式转换首先执行:Boolean("false") -> true。
    2. -
    3. @Transform 装饰器执行:此时传递给 booleanTransformer 的 value 已经是上一步错误转换后的布尔值 true,而不是原始的字符串 "false"。转换函数无从下手。
    4. -
    -

    最终结果依然是 true。

    -

    ## 解决方案:用 any 绕过隐式转换

    -

    要解决这个问题,核心在于阻止 class-transformer 进行那次错误的、优先的隐式转换,从而确保自定义 @Transform 函数能接收到最原始的字符串值。

    -

    最直接且侵入性最小的方法,是将 DTO 中相关属性的 TypeScript 类型从 boolean 改为 any。

    -

    修正后的 DTO 定义:

    -
    // product-filter.dto.ts (正确的实现)
    import { Transform } from 'class-transformer';
    import { IsBoolean, IsOptional } from 'class-validator';
    import { booleanTransformer } from './boolean-transformer';

    // 一个更健壮的 booleanTransformer
    export const robustBooleanTransformer = ({ value }: { value: string }) =>
    value === 'true' ? true : value === 'false' ? false : value;

    export class ProductFilterDto {
    @IsOptional()
    @Transform(robustBooleanTransformer)
    @IsBoolean()
    hasStock?: any; // <-- 类型从 boolean 改为 any

    @IsOptional()
    @Transform(robustBooleanTransformer)
    @IsBoolean()
    isFeatured?: any; // <-- 类型从 boolean 改为 any
    }
    -

    这个改动虽然看起来放弃了 TypeScript 的类型检查,但在这个特定的场景下,它非常安全且有效。原因如下:

    -
      -
    1. 阻止隐式转换:当 class-transformer 看到属性类型是 any 时,它不知道该隐式转换成什么目标类型,因此会“跳过”这个属性的隐式转换步骤。
    2. -
    3. @Transform 接管:如此一来,原始的字符串值("true" 或 "false")就能原封不动地传递给 robustBooleanTransformer 函数。该函数现在可以正确地将字符串转换为期望的布尔值。
    4. -
    5. @IsBoolean 守门:在自定义转换完成后,@IsBoolean() 装饰器会进行最后的验证,确保存入 DTO 的最终值必须是 true 或 false。这保证了在业务逻辑中,该属性的类型是绝对安全的。
    6. +
        +
      • 三环类抗抑郁药 (TCAs)(如阿米替林): 低剂量使用可以有效缓解IBS的慢性腹痛,其抗胆碱能作用也有助于控制腹泻。
      • +
      • 选择性5-羟色胺再摄取抑制剂 (SSRIs)(如氟西汀、帕罗西汀): 可用于伴有便秘和焦虑的IBS患者。
      • +
      +
        +
      1. 益生菌 (Probiotics)
      -

      好用,爱用。

      +

      补充特定的益生菌菌株(如某些双歧杆菌和乳酸杆菌)有助于恢复肠道微生态平衡,改善腹胀和总体症状。不同菌株效果各异,需根据产品说明和医生建议选用。

      +]]> + + Medicine + + + + libdecor-gtk-WARNING Failed to initialize GTK in VSCode on Wayland + /2025/03/30/libdecor-gtk-WARNING-Failed-to-initialize-GTK/ + 在 Wayland 上的 VSCode 中使用 libdecor-gtk 时,可能会遇到以下警告:

      +
      libdecor-gtk-WARNING: Failed to initialize GTK
      +

      这是因为 VSCode 默认使用 X11,可以使用以下参数启动 VSCode:

      +
      code --enable-features=UseOzonePlatform --ozone-platform=wayland
      +]]>
      + + Technique + +
      + + Linux 下 AMD 显卡屏幕画面闪烁问题修复方案 + /2025/04/20/linux-amd-screen-boom/ + 不知道我们所说的闪烁是不是一个东西,在我的笔记本上,闪烁是指偶发的屏幕中出现部分彩色雪花。

      +

      我使用 Pop!_OS,此发行版使用 EFI 启动,故而可以在 /boot/efi/loader/loader.conf 中加上:

      +
      options quiet splash amdgpu.dcdebugmask=0x10 amdgpu.sg_display=0
      +

      对于其他使用 EFI 或 GRUB 启动的发行版也可以添加类似的参数尝试尝试。

      ]]>
      Technique @@ -3369,6 +3317,58 @@ A relation table (also sometimes called a JOIN, link or pi
    7. BullMQ Guide & Patterns · Process Step Jobs (completed event only fires after process resolves)
      https://docs.bullmq.io/patterns/process-step-jobs
    8. +]]> + + Technique + +
      + + NestJS 中 enableImplicitConversion 与 @Transform 的冲突 + /2025/08/13/nestjs-enableImplicitConversion-and-transform/ + 在 NestJS 生态中,class-validator 和 class-transformer 这两个库提供了以声明式的方式对 DTO 进行验证和转换。然而在处理布尔值时,如果在全局验证管道或仅仅是在局部同时开启了 enableImplicitConversion,可能会引入一个极其隐蔽且违反直觉的 Bug:前端传过来的布尔值恒为 true。

      +

      一个简单的筛选功能

      +

      假设正在开发一个电子商务平台的 API,需要实现一个产品列表的筛选功能。希望能够根据产品是否有库存 (hasStock)、是否为特色产品 (isFeatured) 等布尔条件进行筛选。

      +

      前端发出的请求 URL 可能如下所示: /products?filter[hasStock]=true&filter[isFeatured]=false

      +

      在 NestJS 后端,首先会在 main.ts 中配置一个全局的 ValidationPipe,以自动处理 DTO 的验证和转换。为了方便,通常会启用 enableImplicitConversion,期望它能自动将 URL 查询参数中的字符串(如 "123", "true")转换为 DTO 中定义的类型(number, boolean):

      +
      // main.ts
      import { ValidationPipe } from '@nestjs/common';
      import { NestFactory } from '@nestjs/core';
      import { AppModule } from './app.module';

      async function bootstrap() {
      const app = await NestFactory.create(AppModule);

      app.useGlobalPipes(
      new ValidationPipe({
      transform: true, // 启用转换
      whitelist: true,
      forbidNonWhitelisted: true,
      transformOptions: {
      // 启用基于 TypeScript 类型的隐式转换
      enableImplicitConversion: true,
      },
      }),
      );

      // ... 其他配置
      await app.listen(3000);
      }
      bootstrap();
      +

      接着,定义一个 ProductFilterDto 来接收这些筛选条件。

      +

      一个看似正确的 DTO 定义:

      +
      // product-filter.dto.ts
      import { IsBoolean, IsOptional } from 'class-validator';

      export class ProductFilterDto {
      @IsOptional()
      @IsBoolean()
      hasStock?: boolean;

      @IsOptional()
      @IsBoolean()
      isFeatured?: boolean;
      }
      +

      在控制器中使用这个 DTO:

      +
      // products.controller.ts
      @Controller('products')
      export class ProductsController {
      @Get()
      find(@Query('filter') filter: ProductFilterDto) {
      // 期望 filter.isFeatured 的值为 boolean false
      console.log(filter);
      // ... 业务逻辑
      }
      }
      +

      当请求 .../products?filter[isFeatured]=false 到达时,本来期望在 find 方法中得到的 filter.isFeatured 的值是布尔类型的 false。然而,控制台输出的结果却令人意外:{ isFeatured: true }

      +

      问题剖析

      +

      这个问题的根源在于 class-transformer 内部的转换执行顺序,以及 JavaScript 中 Boolean 函数的类型转换行为。

      +

      所有通过 URL 查询参数传递的值,其本质都是字符串。当 NestJS 接收到请求时,filter.isFeatured 的原始值是字符串 "false"。

      +

      ValidationPipe 启动 class-transformer 的转换流程。由于在全局管道中设置了 enableImplicitConversion: true,转换器会首先检查 DTO 属性的 TypeScript 类型。

      +
        +
      1. 隐式转换优先执行:class-transformer 看到 ProductFilterDto 中的 isFeatured 属性被声明为 boolean 类型。
      2. +
      3. 错误的类型转换:它立即尝试将字符串 "false" 转换为布尔值。这个转换等同于执行 Boolean("false")。在 JavaScript 中,任何非空字符串(包括 "false")通过 Boolean() 构造函数转换后都会得到 true。
      4. +
      5. 结果覆盖:这个错误的 true 值被作为该属性的转换结果。
      6. +
      +

      此时,即使尝试添加一个自定义的 @Transform 装饰器来手动处理这个问题,也为时已晚。

      +

      例如,定义一个 booleanTransformer:

      +
      // boolean-transformer.ts
      export const booleanTransformer = ({ value }: { value: any }) => {
      if (typeof value === 'string') {
      return value === 'true';
      }
      return value;
      };
      +

      然后更新 dto:

      +
      // product-filter.dto.ts (错误的尝试)
      import { Transform } from 'class-transformer';
      import { IsBoolean, IsOptional } from 'class-validator';
      import { booleanTransformer } from './boolean-transformer';

      export class ProductFilterDto {
      // ...
      @IsOptional()
      @Transform(booleanTransformer) // 添加自定义转换
      @IsBoolean()
      isFeatured?: boolean;
      }
      +

      流程会变成这样:

      +
        +
      1. 隐式转换首先执行:Boolean("false") -> true。
      2. +
      3. @Transform 装饰器执行:此时传递给 booleanTransformer 的 value 已经是上一步错误转换后的布尔值 true,而不是原始的字符串 "false"。转换函数无从下手。
      4. +
      +

      最终结果依然是 true。

      +

      ## 解决方案:用 any 绕过隐式转换

      +

      要解决这个问题,核心在于阻止 class-transformer 进行那次错误的、优先的隐式转换,从而确保自定义 @Transform 函数能接收到最原始的字符串值。

      +

      最直接且侵入性最小的方法,是将 DTO 中相关属性的 TypeScript 类型从 boolean 改为 any。

      +

      修正后的 DTO 定义:

      +
      // product-filter.dto.ts (正确的实现)
      import { Transform } from 'class-transformer';
      import { IsBoolean, IsOptional } from 'class-validator';
      import { booleanTransformer } from './boolean-transformer';

      // 一个更健壮的 booleanTransformer
      export const robustBooleanTransformer = ({ value }: { value: string }) =>
      value === 'true' ? true : value === 'false' ? false : value;

      export class ProductFilterDto {
      @IsOptional()
      @Transform(robustBooleanTransformer)
      @IsBoolean()
      hasStock?: any; // <-- 类型从 boolean 改为 any

      @IsOptional()
      @Transform(robustBooleanTransformer)
      @IsBoolean()
      isFeatured?: any; // <-- 类型从 boolean 改为 any
      }
      +

      这个改动虽然看起来放弃了 TypeScript 的类型检查,但在这个特定的场景下,它非常安全且有效。原因如下:

      +
        +
      1. 阻止隐式转换:当 class-transformer 看到属性类型是 any 时,它不知道该隐式转换成什么目标类型,因此会“跳过”这个属性的隐式转换步骤。
      2. +
      3. @Transform 接管:如此一来,原始的字符串值("true" 或 "false")就能原封不动地传递给 robustBooleanTransformer 函数。该函数现在可以正确地将字符串转换为期望的布尔值。
      4. +
      5. @IsBoolean 守门:在自定义转换完成后,@IsBoolean() 装饰器会进行最后的验证,确保存入 DTO 的最终值必须是 true 或 false。这保证了在业务逻辑中,该属性的类型是绝对安全的。
      6. +
      +

      好用,爱用。

      ]]>
      Technique @@ -3491,6 +3491,17 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 Life
      + + shadow-cljs 怎么设置代理 + /2024/04/03/shadow-cljs-proxy/ + The library pomegranate used for retrieving maven dependencies does not read .m2/settings.xml. You can however set the :proxy config either in shadow-cljs.edn directly or ~/.shadow-cljs/config.edn.

      +

      See https://github.com/cemerick/pomegranate/blob/master/src/main/clojure/cemerick/pomegranate/aether.clj#L757-L765.

      +

      shadow-cljs.edn:

      +
      {:source-paths ["src/main"
      "src/test"]

      :dependencies [[reagent "1.2.0"]
      [re-frame "1.3.0"]]

      ;; Here
      :proxy {:host "localhost"
      :port 20171}

      :builds {:app {:target :react-native
      :init-fn example.app/init
      :output-dir "app"
      :compiler-options {:infer-externs :auto}
      :devtools {:autoload true}}}}
      ]]>
      + + Technique + +
      表面活性剂 /2025/10/28/sufactants/ @@ -3506,17 +3517,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 Life - - shadow-cljs 怎么设置代理 - /2024/04/03/shadow-cljs-proxy/ - The library pomegranate used for retrieving maven dependencies does not read .m2/settings.xml. You can however set the :proxy config either in shadow-cljs.edn directly or ~/.shadow-cljs/config.edn.

      -

      See https://github.com/cemerick/pomegranate/blob/master/src/main/clojure/cemerick/pomegranate/aether.clj#L757-L765.

      -

      shadow-cljs.edn:

      -
      {:source-paths ["src/main"
      "src/test"]

      :dependencies [[reagent "1.2.0"]
      [re-frame "1.3.0"]]

      ;; Here
      :proxy {:host "localhost"
      :port 20171}

      :builds {:app {:target :react-native
      :init-fn example.app/init
      :output-dir "app"
      :compiler-options {:infer-externs :auto}
      :devtools {:autoload true}}}}
      ]]>
      - - Technique - -
      使用 Runed intersection observer 与 tanstack query 实现通用的惰性加载组件 /2025/07/31/svelte-lazyquery/ @@ -3739,18 +3739,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 Medicine - - 二〇二三年八月一日 - /2023/08/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ - 阳光给他病态一般白的皮肤注入了一些生机,左手手臂上斑驳的伤痕,每一道都诉诸着他对家人浓重的思念。那是在暗无天日的岁月里,他通过痛楚得到的慰藉。

      -

      时光对谁都不温柔,愿意对我温柔的,只有身边陪我一起迎接时光洗礼的人。

      -

      宇宙中也不会有什么声音, 视觉上再震撼的毁灭也只是发生在沉默之中。

      -

      一切重新沉寂下来。

      -]]>
      - - gallery - -
      乙酰半脱氨酸的药理学与药代动力学特性 /2025/12/30/%E4%B9%99%E9%85%B0%E5%8D%8A%E8%84%B1%E6%B0%A8%E9%85%B8%E7%9A%84%E8%8D%AF%E7%90%86%E5%AD%A6%E4%B8%8E%E8%8D%AF%E4%BB%A3%E5%8A%A8%E5%8A%9B%E5%AD%A6%E7%89%B9%E6%80%A7/ @@ -3783,6 +3771,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 Medicine + + 二〇二三年八月一日 + /2023/08/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ + 阳光给他病态一般白的皮肤注入了一些生机,左手手臂上斑驳的伤痕,每一道都诉诸着他对家人浓重的思念。那是在暗无天日的岁月里,他通过痛楚得到的慰藉。

      +

      时光对谁都不温柔,愿意对我温柔的,只有身边陪我一起迎接时光洗礼的人。

      +

      宇宙中也不会有什么声音, 视觉上再震撼的毁灭也只是发生在沉默之中。

      +

      一切重新沉寂下来。

      +]]>
      + + gallery + +
      二〇二三年六月二十九日 /2023/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/ @@ -3870,18 +3870,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

      那在这之前呢?

      在这之前我刚吃完一碗辣椒炒肉,

      香

      -]]> - - gallery - -
      - - 二〇二五年二月二十四日 - /2025/02/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/ - 街上的人躲进建筑,道路上的车变得缓慢。路边的黑猫坐在屋檐下张望,在雨天,它也有了思考的地方与时间。除了淅淅沥沥的雨声再无其他。

      -

      衣服买大了,他们走路好急,地铁上只有老人没看手机,天怎么黑那么快是我起晚了吗,那个蹦蹦跳跳的女孩耳机里在听什么歌,我还在原地吗,我要再等等吗。

      -

      家就像厚酒之后突然明晰的想象,时间不语,只是沉默,我沉默地走进时间,看着我和你的年龄相差越来越小。

      -

      这雨天,世界都温柔的可以掐出水来。

      ]]>
      gallery @@ -3930,6 +3918,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

      虽然我早已不强求世界上有这么一个人,

      但我会一直等待,甚至期待你的到来。

      我叫韩暮秋,幸会。

      +]]> + + gallery + +
      + + 二〇二五年二月二十四日 + /2025/02/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/ + 街上的人躲进建筑,道路上的车变得缓慢。路边的黑猫坐在屋檐下张望,在雨天,它也有了思考的地方与时间。除了淅淅沥沥的雨声再无其他。

      +

      衣服买大了,他们走路好急,地铁上只有老人没看手机,天怎么黑那么快是我起晚了吗,那个蹦蹦跳跳的女孩耳机里在听什么歌,我还在原地吗,我要再等等吗。

      +

      家就像厚酒之后突然明晰的想象,时间不语,只是沉默,我沉默地走进时间,看着我和你的年龄相差越来越小。

      +

      这雨天,世界都温柔的可以掐出水来。

      ]]>
      gallery @@ -4091,20 +4091,20 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的
      - 二〇二五年十月三十一日 - /2025/10/31/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%89%E5%8D%81%E4%B8%80%E6%97%A5/ - 傍晚的海风带着盐的咸味和鱼网的腥气吹上岸。她独自站在礁石上眺望。巨大的海面起伏不定,深蓝的尽头与灰暗的天空交融成一个模糊的线。巨大的落日悬浮其上,像一颗即将坠入海面的橙红心脏。霞光将她的脸庞染成暖色。海风掀起她披在肩上的丝巾,丝巾像一面旗帜在身后猎猎作响。她深深吸了一口气,咸冷的空气涌入肺腑,带着一种辽阔的清醒。

      -

      时间会带走了一个人,也带走了他的呼吸,他的声音,他掌心的温度。但似乎又留下了些什么

      -

      光的颗粒在空气里浮动,在记忆里游走,她闭上眼睛,光从睫毛上滑过,像许多个早晨,像许多个傍晚,像她们再次相遇的无数个灿然的瞬间。她在其中走走停停,像一粒微尘,又像一颗星。

      + 二〇二五年十二月十九日 + /2025/12/19/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E5%8D%81%E4%B9%9D%E6%97%A5/ + 一个尚未开始就已葬于山海的情欲,在无人知晓的深夜,完成了它短暂一生中最私密的绽放。

      ]]>
      gallery
      - 二〇二五年十二月十九日 - /2025/12/19/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E5%8D%81%E4%B9%9D%E6%97%A5/ - 一个尚未开始就已葬于山海的情欲,在无人知晓的深夜,完成了它短暂一生中最私密的绽放。

      + 二〇二五年十月三十一日 + /2025/10/31/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%89%E5%8D%81%E4%B8%80%E6%97%A5/ + 傍晚的海风带着盐的咸味和鱼网的腥气吹上岸。她独自站在礁石上眺望。巨大的海面起伏不定,深蓝的尽头与灰暗的天空交融成一个模糊的线。巨大的落日悬浮其上,像一颗即将坠入海面的橙红心脏。霞光将她的脸庞染成暖色。海风掀起她披在肩上的丝巾,丝巾像一面旗帜在身后猎猎作响。她深深吸了一口气,咸冷的空气涌入肺腑,带着一种辽阔的清醒。

      +

      时间会带走了一个人,也带走了他的呼吸,他的声音,他掌心的温度。但似乎又留下了些什么

      +

      光的颗粒在空气里浮动,在记忆里游走,她闭上眼睛,光从睫毛上滑过,像许多个早晨,像许多个傍晚,像她们再次相遇的无数个灿然的瞬间。她在其中走走停停,像一粒微尘,又像一颗星。

      ]]>
      gallery @@ -4271,18 +4271,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的
      - 二〇二四年九月十二日 - /2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/ - 人只不过是一根苇草,是自然界最脆弱的东西,但却是一根能思想的苇草。用不着整个宇宙都拿起武器来才能毁灭人类,一口气、一滴水就足以致人类于死命了。然而纵使宇宙毁灭了人类,在人类的视角下自己却仍然要比致其于死命的东西高贵得多,因为人类知道自己要死亡,以及宇宙对其所具有的优势,还可以主动地认为宇宙对此一无所知。

      + 二〇二四年九月一日 + /2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/ + 通过忠实地映射宇宙来隐藏自我,是融入永恒的唯一途径。

      ]]>
      gallery
      - 二〇二四年九月一日 - /2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/ - 通过忠实地映射宇宙来隐藏自我,是融入永恒的唯一途径。

      + 二〇二四年九月十二日 + /2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/ + 人只不过是一根苇草,是自然界最脆弱的东西,但却是一根能思想的苇草。用不着整个宇宙都拿起武器来才能毁灭人类,一口气、一滴水就足以致人类于死命了。然而纵使宇宙毁灭了人类,在人类的视角下自己却仍然要比致其于死命的东西高贵得多,因为人类知道自己要死亡,以及宇宙对其所具有的优势,还可以主动地认为宇宙对此一无所知。

      ]]>
      gallery @@ -4340,6 +4340,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 二〇二四年五月十六日 /2024/05/16/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%94%E6%9C%88%E5%8D%81%E5%85%AD%E6%97%A5/ 我最好趁早学会尊重客观规律。

      +]]>
      + + gallery + +
      + + 二〇二四年八月一日 + /2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ + 阿公烧烟成老瘾,
      +阿公饮酒会面红,
      +他说日子要开心,
      +他说人生要尽兴。

      ]]>
      gallery @@ -4363,18 +4375,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

      早上七点。

      太阳完全绽放的光芒照亮了这个欣欣向荣的世界,有山、有海,有自由。

      -]]> - - gallery - -
      - - 二〇二四年八月一日 - /2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ - 阿公烧烟成老瘾,
      -阿公饮酒会面红,
      -他说日子要开心,
      -他说人生要尽兴。

      ]]>
      gallery @@ -4475,15 +4475,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

      世界很大,大到容纳下云海山川与深海,一定要去看看所有的色彩。世界也很小,小到只能让我一个人存在,原地转一圈,就足以填补了人生所有的空白。

      我的家人太早回归了群星,而我还贪恋着人间烟火。

      息止安所。

      -]]> - - gallery - -
      - - 二零二三年二月一日 - /2023/02/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%80%E6%97%A5/ - 我想在六点和日出打招呼,我想去菜市场看看卖蔬菜的阿婆,我想花一上午去准备一餐中午饭,我想下午能在阳台捧着一盏热茶慢慢看书,再慢点,慢到能透过阳台栏杆的缝隙看看马路上车水马龙映衬下的老人家提着新鲜的一荤一素蹒跚走回家,看看贼几把大的夕阳在冒着热气的水杯中慢慢融化。

      ]]>
      gallery @@ -4497,6 +4488,15 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 在后排,其他人也各自找好位置挺直脊背站着。

      “拍了啊,倒数,三、二、一……茄子!”

      咔擦,画面定格,幸福定格,大家终于从苦难里彻底毕业了。

      +]]> + + gallery + +
      + + 二零二三年二月一日 + /2023/02/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%80%E6%97%A5/ + 我想在六点和日出打招呼,我想去菜市场看看卖蔬菜的阿婆,我想花一上午去准备一餐中午饭,我想下午能在阳台捧着一盏热茶慢慢看书,再慢点,慢到能透过阳台栏杆的缝隙看看马路上车水马龙映衬下的老人家提着新鲜的一荤一素蹒跚走回家,看看贼几把大的夕阳在冒着热气的水杯中慢慢融化。

      ]]>
      gallery @@ -4635,18 +4635,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的
      - 二零二二年十一月月三日 - /2022/11/03/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E4%B8%80%E6%9C%88%E6%9C%88%E4%B8%89%E6%97%A5/ - 我独自冒着冷风,去薄霜铺地的林子里,为听鸟语,为盼朝阳,为寻泥土里渐次苏醒的花草,但春信不至。我是如此单独而完整,在无数个夜晚,独自顶着冷风 伫立在老橘树下的桥头,只为听一曲夜莺的哀歌,倚暖了石栏上的青苔,青苔凉透了我的心坎,但夜莺不来,夜莺不来。

      + 二零二二年八月二十一日 + /2022/08/21/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%85%AB%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%80%E6%97%A5/ + 不管是什么样的生活,都只是为了让自己满意,我这样活着,没什么不满的,我很痛快。这世上所有东西,我留意它时,它就存在,不留意时,它就是虚无。幸运的是我留意的事物很少,不致于身心劳碌一生奔忙。岁月会锤炼我一副丰满的灵魂和清瘦的欲望。

      ]]>
      gallery
      - 二零二二年八月二十一日 - /2022/08/21/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%85%AB%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%80%E6%97%A5/ - 不管是什么样的生活,都只是为了让自己满意,我这样活着,没什么不满的,我很痛快。这世上所有东西,我留意它时,它就存在,不留意时,它就是虚无。幸运的是我留意的事物很少,不致于身心劳碌一生奔忙。岁月会锤炼我一副丰满的灵魂和清瘦的欲望。

      + 二零二二年十一月月三日 + /2022/11/03/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E4%B8%80%E6%9C%88%E6%9C%88%E4%B8%89%E6%97%A5/ + 我独自冒着冷风,去薄霜铺地的林子里,为听鸟语,为盼朝阳,为寻泥土里渐次苏醒的花草,但春信不至。我是如此单独而完整,在无数个夜晚,独自顶着冷风 伫立在老橘树下的桥头,只为听一曲夜莺的哀歌,倚暖了石栏上的青苔,青苔凉透了我的心坎,但夜莺不来,夜莺不来。

      ]]>
      gallery @@ -6207,6 +6207,69 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的
    +]]>
    + + Medicine + +
    + + 房颤心电图解析:从 P 波消失到不规则心室节律 + /2026/08/20/%E6%88%BF%E9%A2%A4%E5%BF%83%E7%94%B5%E5%9B%BE%E8%A7%A3%E6%9E%90/ + 房颤的心电图识别,主要依靠三个关系:P 波是否存在、R-R 间期是否规则、QRS 波群是否增宽。其中,P 波消失和心室节律绝对不规则最支持房颤;QRS 波群的宽窄则帮助判断心室内传导情况,但不能单独用来判断患者是否安全。

    +

    先看一条标准心电图

    +

    临床诊断房颤需要记录到典型心律,再由医生结合病史确认。2024 年欧洲房颤指南把房颤诊断、卒中预防、症状控制和危险因素管理放在同一个长期管理框架中[1]。

    +

    一张常规十二导联心电图首先观察三件事:有没有规律出现的 P 波;相邻 QRS 波群之间的 R-R 间期是否长短不一;QRS 波群是否增宽。三个特征需要一起判断。单独看到心率快、一次 R-R 间期变化,或者某个导联中的 P 波不清楚,都不足以完成房颤诊断。

    +

    第一处:P 波消失,基线出现细小波动

    +

    窦性心律中,心房由窦房结发出有序电活动。每次心室激动前,心电图通常可以看到一个形态相对稳定的 P 波。P 波代表心房去极化,随后电活动经过房室结传向心室,形成 QRS 波群。

    +

    房颤时,心房电活动快速而无序,不能形成一次有效的整体收缩。心电图上常见的表现是找不到持续、规则、形态稳定的 P 波,QRS 波群之间的基线出现细小而不规则的波动。不同导联中的基线波动清晰度可能不同,肌肉震颤和电极接触不良也可能制造类似干扰。

    +

    因此,“没有看到 P 波”不能机械地等同于房颤。需要把纸速、导联、基线质量和 R-R 间期一起纳入判断。

    +

    第二处:R-R 间期绝对不规则

    +

    房颤的另一个核心特征是心室节律不规则。由于房室结接收到的心房冲动没有固定规律,传到心室的冲动间隔会发生变化,表现为 R-R 间期长短不一,而且通常不存在可重复的规律。

    +

    这和呼吸性窦性心律不齐不同。后者的 R-R 间期变化往往与呼吸周期有关,仍然可以找到规律的 P 波。房性早搏、室性早搏、房扑伴不规则传导和部分多源性房性心动过速,也可能造成节律不齐,需要结合完整心电图鉴别。

    +

    心率是平均值,不能代替节律判断。一次记录显示 116 次/分,只能说明这段记录中的平均心室率偏快;它不能单独证明房颤,也不能单独判断症状严重程度。

    +

    第三处:QRS 波群窄,不等于房颤风险低

    +

    QRS 波群反映心室去极化。若 QRS 时限相对较窄,通常说明这次心室激动主要沿正常或接近正常的传导路径下传;若 QRS 明显增宽,则需要考虑束支传导阻滞、室内传导延迟、预激或室性心律等情况。

    +

    房颤可以伴窄 QRS,也可以伴宽 QRS。快速房颤伴原有束支阻滞、差异性传导或预激时,QRS 可能增宽。因此,窄 QRS 只能说明当前记录中的室内传导没有明显增宽,不能推出“不会发生心脏骤停”,也不能替代对卒中、心衰和基础心脏病的评估。

    +

    交互示意:把三处观察点分开看

    +

    下面的图是教学示意,不是任何患者原始心电图的逐点重建。点击按钮后,可以分别突出 P 波、R-R 间期和 QRS 波群。

    +
    + +

    交互示意:窦性心律与房颤的三个观察点

    教学示意|不代表任何患者的原始心电图

    ECG SKETCH
    +
    +
    窦性心律房颤示意P 波规则出现R-R 间期规则P 波消失,基线细小波动R-R 间期长短不一QRS
    +
    总览:房颤示意中可以同时观察 P 波缺失、R-R 间期不规则和 QRS 波群形态。三者需要结合判断。
    红色:房颤示意绿色:窦性心律或相对窄的 QRS蓝色:节律间期标注
    + +
    +

    房颤和房扑不能混为一谈

    +

    房扑也可能造成心房活动异常和心室节律不齐,但其心房电活动常呈现较规则的锯齿样扑动波,尤其在下壁导联中更容易观察到。房颤的基线活动通常更加无序,R-R 间期也更不规则。不过,真实心电图可能受到心率、房室传导比例、药物、导联质量和合并心律失常影响,不能只凭一小段波形截图下结论。

    +

    手表提示“可能房颤”,下一步做什么

    +

    智能手表和手环可以帮助发现异常节律,但“设备提示”与“临床确诊”不是同一件事。Apple Heart Study 纳入 419,297 名参与者;研究结果显示,设备提示具有筛查价值,但提示本身不等于确诊[2]。

    +

    具有单导联心电图功能的设备,能够提供比单纯脉搏波更接近心电信号的记录。研究显示,单导联设备在部分心律识别任务中有帮助,但对不同类型的室上性心动过速,判读准确度仍低于标准十二导联心电图[3]。

    +

    如果设备反复提示可能存在房颤,应保存原始记录,记录发生时间、持续时间和伴随症状,并到心内科接受标准心电图或动态心电图评估。不要依据手表提示自行开始抗凝,也不要因为一次“未发现房颤”就认为以后不会发生房颤。

    +

    房颤心电图之后,还要评估什么

    +

    心电图回答的是“这段时间有没有记录到怎样的心律”。它不能单独回答患者长期卒中风险、出血风险、心脏结构和诱发因素。

    +

    确诊或高度怀疑房颤后,医生还需要结合年龄、既往卒中或短暂性脑缺血发作、高血压、糖尿病、心力衰竭、血管疾病、肾功能、贫血、甲状腺功能和用药情况,判断后续检查与治疗方向。抗凝、控制心率、节律控制和危险因素管理需要放在同一套决策中,不能只盯着心率数字。

    +

    如果同时出现单侧肢体无力、口角歪斜、言语含糊、突发视物异常、持续胸痛、晕厥或严重呼吸困难,应立即急诊处理。即使症状很快缓解,也不能排除短暂性脑缺血发作或其他急症。

    +

    结语

    +

    房颤心电图的识别可以从三个问题开始:有没有规则 P 波,R-R 间期是否绝对不规则,QRS 波群是否增宽。最有价值的判断来自这些特征的组合,而不是某一个数字或某一个设备提示。

    +

    心电图确认的是当时记录到的电活动。患者是否需要抗凝、控制心率、恢复节律或进一步检查,还要结合卒中风险、出血风险、心脏结构和基础疾病综合决定。

    +
    +

    参考文献

    +
    +
    +
      +
    1. Van Gelder IC, et al. 2024 ESC Guidelines for the management of atrial fibrillation developed in collaboration with the EACTS. European Heart Journal. 2024;45(36):3314-3414. PMID: 39210723. DOI: 10.1093/eurheartj/ehae176. ↩︎

      +
    2. +
    3. Perez MV, et al. Large-Scale Assessment of a Smartwatch to Identify Atrial Fibrillation. New England Journal of Medicine. 2019;381:1909-1917. PMID: 31722151. DOI: 10.1056/NEJMoa1901183. ↩︎

      +
    4. +
    5. Vukadinovic D, et al. Diagnostic accuracy of Apple Watch Series 6 recorded single-lead ECGs for identifying supraventricular tachyarrhythmias: a comparative analysis with invasive electrophysiological study. Europace. 2024. PMID: 37985539. ↩︎

      +
    6. +
    +
    ]]>
    Medicine -- cgit v1.2.3