feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)

Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
This commit is contained in:
Z User committed 2026-10-27 22:50:50 +00:00
1 parent 7acc0c5451
commit e45b68bb8b
1089 files changed
+260614

No files matched your search

+413
View File
@@ -0,0 +1,413 @@
# 标点全规则 Punctuation:风格 / 挤压 / 避头尾 / 破折号
中文排版的"暗物质"。占文本面积 5%–15%,决定 95% 的"专业感"。本文按 6 个主题展开:4 种标点风格、加法/减法模式、4 级避头尾、行内挤压/拉伸优先级、破折号专题、Unicode 码位速查。
## 一、4 种标点风格
不要混用。一个项目锁定一种风格。
### 1. 全角式(Full-width)
- 所有标点占 1 个汉字宽
- 出现场景:默认网页排版(引擎不调)、《普列汉诺夫美学论文选》等老派出版
- 优点:纵横对齐严格
- 缺点:标点连用形成"白窟窿"(` "。( ` 三符 ≈ 3 格空白)
- CSS:默认行为(无需特别处理)
### 2. 半角式(Half-width)
- 所有标点占 0.5 个汉字宽
- 出现场景:辞典(《汉语大字典》节约空间)、苹果中国官网 2018 年(用 WebFont SF Pro SC)
- 优点:紧凑,西文混排自然
- 缺点:句末停顿感弱,影响阅读节奏
- CSS:`font-feature-settings: "halt" 1;` 或换用半宽字体
### 3. 开明式(Kaiming-style,大陆纸媒主流)
- **句末标点全宽**:`。 ? !`(句号、问号、叹号)
- **句内标点半宽**:`, 、 ; :`(逗号、顿号、分号、冒号)
- 出现场景:大陆纸媒、方正飞腾默认、绝大多数大陆图书
- **推荐用于大陆现代横排正文**
- CSS:手动控制或用 InDesign 标点挤压预设
### 4. 居中式(Centered,港台主流)
- 所有标点全宽 + 字面正中
- 同时适应横排和直排(标点位置一致)
- 出现场景:港台几乎所有出版物、传统书法集
- 优点:方向无关、对纵横对齐友好
- 缺点:横排时"。"看着像浮在中间
- CSS:港台地区字体(如华康、文鼎)自带这种字面分布
### 选择决策表
| 项目类型 | 推荐风格 | 备注 |
|---|---|---|
| 大陆现代图书 / 杂志 / 报刊 | 开明式 | 主流选择 |
| 大陆学术论文 / 招股书 / 公文 | 开明式 | 严肃 |
| 港台繁体书籍 | 居中式 | 民国延续 |
| 字典 / 辞海 / 工具书 | 半角式 | 节约空间 |
| 网页正文(无引擎支持时) | 全角式(被迫) | 加 `text-spacing-trim` 可改善 |
| 现代网页 / App(有引擎) | 开明式 + `text-spacing-trim` | 推荐 |
| 古籍影印 / 文言 | 居中式或全角式 | 配直排 |
## 二、加法模式 vs 减法模式
**这是认知误区的重灾区。**
### 加法模式(Additive,专业排版的方式)
- 标点本身 = **半宽**
- 加上一侧或两侧的**空隙**凑成全宽
- 调整对象 = 空隙(可拿掉、可缩半、可拉满)
- 采用方:InDesign、QuarkXPress、JIS、Adobe 全系
- 标点连用时:把相邻一侧的空隙拿掉
### 减法模式(Subtractive,大众默认认知)
- 标点本身 = **全宽**
- 通过"挤压"去掉部分宽度
- 调整对象 = 标点字面本身
- 采用方:方正自家排版软件、Word 中文版部分预设
- 问题:在加法模式的字体上做减法会算错
### 实操判断
**用 InDesign / Adobe 系统**:默认加法模式。选字体时优先选标点"偏靠式"的字库(思源、华文、蒙纳);规避方正大量正文字体(方正新书宋、方正悠黑、方正宋一)—— 它们把标点摆在字面正中("居中式"),与 Adobe 算法错位。
**用方正飞腾**:默认减法模式,方正字库内部自洽,不会出问题。
**Web / CSS**:现代 `text-spacing-trim: space-all` 是加法模式实现。
### 加法模式下标点的 3 类分组
| 类别 | 标点 | 空隙 |
|---|---|---|
| **第 1 类**:默认密排不加空 | 着重号、分隔号、连接号短横、日文 `?!` | 0 |
| **第 2 类**:一边加二分空(大陆 + 日文绝大多数) | 大陆问号 / 叹号 / 冒号 / 分号 / 逗号 / 顿号 / 句号;蝌蚪引号 ""'';圆括号 ();书名号《》 | 半宽 + 二分空 = 全宽 |
| **第 3 类**:两边各加四分空(港台多数 + 日文中点) | 港台问号 / 叹号 / 冒号;日文 `・` | 半宽 + 左 1/4 + 右 1/4 = 全宽 |
## 三、行末半角原则(GB/T 15834-2011 §5.1.10)
**强制规则**:标点排在一行末尾时,若为全宽字符**应占半角字符的宽度**。即"行末标点 → 半宽"。
- 这是中国大陆和多数中国出版社的硬性规范
- 视觉效果:行尾对齐(不会因末标点全宽撑出视觉外凸)
- 实现:InDesign "标点挤压" → 行尾减半(自动);CSS `hanging-punctuation: allow-end` 让标点悬挂到版心外
## 四、避头尾 4 级严格度(W3C CLReq §6.1.1)
**同一文档内必须统一一种严格度。**
### 0 级:none(不处理)
- 不做任何避头尾
- 见于港台部分报刊(标点居中无突兀)
- 大陆不推荐
### 1 级:basic(基本处理,最低标准)
- **不可在行首**:`。, 、 ; : ! ? … 》 」 』 ) 】`、所有"结束"括号和引号、连接号 `–`、间隔号 `·`、分隔号 `/`
- **不可在行尾**:`( 【 《 「 『 " '`、所有"开始"括号和引号
### 2 级:GB 法(大陆国标,推荐用于正式出版)
- basic 全部规则
- **加**:分隔号 `/` 也不可在行尾
- 这是中国大陆图书业的默认级别
### 3 级:strict(严格,推荐用于精装/正式出版)
- GB 法全部规则
- **加**:破折号 `——` 不可在行首
- **加**:省略号 `……` 不可在行首
- InDesign "简体中文避头尾" 默认即此级
### 完整字符表
#### 不可出现在行首
```
。,、;:?!…—··――
)】〗〕〉》」』〙〗⦆
’"
%‰‱℃℉°′″
/、‧/
```
#### 不可出现在行尾
```
(【〖〔〈《「『〘〖⦅
‘"
$¥€£¢₩₫
```
#### 不可分离(不可在中间断开)
```
—— 破折号(2 字宽视为一体)
…… 省略号(2 字宽视为一体)
123456 阿拉伯数字内部及小数点
50% 数字 + 百分号 / 千分号 / 度数
20℃ 数字 + 单位
±5 符号 + 数字
m² 被标记词 + 上下标
①② 注释记号 + 被注词
word 西文单词(按 hyphenation)
```
### 处理顺序:"先挤进后推出"
W3C 明文规则。
1. **第一步**:检查标点能否通过"宽度挤压"塞回前行末
- 行末全宽标点 → 半宽
- 连续标点 → 拿掉相邻一侧的空隙
- 中西间距 1/4 em → 1/8 em
2. **第二步**:仍塞不下,从前行末字推入下行
3. **第三步**:前行多出的空间按拉伸优先级处理
4. **第四步**:最后才平均加大字间距
**口诀**:**挤进只需腾半字宽,推出至少动一字宽;正文排版应尽量少调**。
### 推出后救济:均排(justify within)
行内调整无法整齐时,按拉伸优先级把空间分配出去。详见下一节。
## 五、行内挤压 / 拉伸优先级(W3C CLReq §6.2.2)
**这是排版引擎的内部算法表,知道了它你才知道为什么 InDesign 那样调字。**
### 挤压优先级(从高到低)
| 优先级 | 对象 | 挤压幅度 |
|---|---|---|
| a | **行末标点** | 固定半字宽(GB/T 15834-2011 §5.1.10) |
| b | 行内西文词距 | 最小 **1/4 em**,多处同等量 |
| c | 间隔号 `·` | 从两侧同等量挤,最小到 0(结果半字宽) |
| d | 夹注符号(括号)| 开始括号前侧、结束括号后侧,最小到半字宽 |
| e | 逗号 / 顿号 / 分号 | 后侧或两侧同等量挤,最小到半字宽 |
| f | 中西间距 | 最小 **1/8 em**(部分风格固定 1/4 em 不允许调) |
| g | 句号 / 问号 / 叹号 | 有空间则挤到半字宽(部分风格不允许调,因句末停顿大) |
### 拉伸优先级(从高到低)
| 优先级 | 对象 | 拉伸幅度 |
|---|---|---|
| a | 西文词距 | 最大 **1/2 em**,多处同等量 |
| b | 中西间距 | 从 1/4 em 默认值起,最大 **1/2 em**(实际多数风格只到 1/3 em)|
| c(最后选项) | 字间距 | 同等量拉,但**不可对禁则字间距拉伸**;连接号、分隔号及其前后字符避免拉伸 |
### 关键数值速查
| 间距 | 最小 | 默认 | 最大 |
|---|---|---|---|
| 西文词距 | 1/4 em | 1/3 em | 1/2 em |
| 中西间距 | 1/8 em | 1/4 em | 1/2 em |
| 间隔号两侧 | 0 | 1/4 em | 1/4 em |
| 行末标点 | 半字宽 | 半字宽 | 半字宽(不变) |
| 连续标点共 2 字 | 1 字 | 1.5 字 | 2 字 |
## 六、连续标点处理(W3C CLReq §6.3.2)
**最常见的灾难现场**:标点连用形成"白窟窿"。
未处理:
```
(明)钟惺、谭元春《唐诗归》钟评后二句"二
句儿童皆诵之,然其言自足不朽"。(清)弘历
↑↑↑
"。( 三个全角占 3 格
```
处理后:
```
(明)钟惺、谭元春《唐诗归》钟评后二句"二句儿童皆
诵之,然其言自足不朽"。(清)弘历《唐宋诗醇》评
↑↑
"。( 挤压到约 1.5 字宽
```
### 规则
- 任意两个相邻标点共 2 字宽时**应缩到 1.5 字宽**
- 可进一步缩到 1 字(但通常不缩到 1 字,避免读者错觉两符号是一个)
- 挤压方向让开始/结束括号紧靠被夹内容
### 实现
- InDesign:段落 → 中文排版设置 → "标点挤压" → 选"简体中文默认"或自定义
- CSS:`text-spacing-trim: space-all`(自动处理)
## 七、破折号专题
**最容易出错的单一符号。**
### 1. 码位优先级
1. **U+2E3A** TWO EM DASH `⸺` —— 正式码位,占 2 字一体,**首选**
2. **U+2014** EM DASH `—` × 2 —— 事实标准,思源字体通过 `ccmp` 替换为长横,**主用**
3. ❌ U+2015 HORIZONTAL BAR `―` —— 简中字库多数会断开
4. ❌ U+2500 BOX DRAWINGS LIGHT HORIZONTAL `─` —— 语义错误(制表符)
5. ❌ U+30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK `ー` —— 日文长音符
6. ❌ U+FF0D FULLWIDTH HYPHEN-MINUS `-` —— 半宽连接符
7. ❌ U+002D HYPHEN-MINUS `-` —— ASCII 连字符
### 2. 字体推荐(破折号正确实现)
**推荐**:
- 思源黑体 / 思源宋体(通过 OpenType `ccmp` 把连续两个 U+2014 替换为整段长横,再用 `locl` 在 CJK 语言下切换为字框居中字形)
- 苹方 PingFang
- HarmonyOS Sans / MiSans
**雷区**(破折号断开 / 顶格 / 下沉):
- 中易宋体、中易仿宋(破折号"无论如何接不上")
- 旧版微软雅黑(破折号下沉且双连超过两字宽)
- 华文黑体(破折号顶格不居中)
- 兰亭黑(破折号顶格且不居中)
### 3. 字体优先级陷阱(CSS)
❌ **错误**:
```css
font-family: "Helvetica", "PingFang SC"; /* 英文字体在前 */
```
连续两个 `—` 会优先取 Helvetica 字形,导致下沉、断开、双横之间留白。
✅ **正确(方案 A)**:删掉英文字体,用系统中文字体
```css
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
```
✅ **正确(方案 B)**:用 `unicode-range` 拆 `@font-face`
```css
@font-face {
font-family: "Helvetica Local";
src: local("Helvetica");
unicode-range: U+0000-007F; /* 只用于 ASCII */
}
body {
font-family: "Helvetica Local", "PingFang SC", sans-serif;
}
```
✅ **正确(方案 C)**:声明 `lang` 激活 `locl`
```html
<html lang="zh-CN">
```
### 4. 防破折号中间断开
**InDesign**:
1. 段落面板 → "禁止断字" 勾选(默认开)
2. 避头尾规则集 → "禁止分开的字符" → 加入 `U+2014`、`U+2015`、`U+2500`
3. 不要只勾"不换行"(那只是局部手动)
**Word**:
- 中文版"后置标点"里的 `―` 实际是 U+2015,不是 U+2014
- 要让 U+2014 也避头尾,须手动加进"自定义后置标点"
- 终极保险:选中单个 `—`,格式→字体→字符间距→缩放 200%,硬把单 em dash 变成两字宽(来自《排版与校对规范》)
**CSS**:
```html
<!-- 两个 em dash 之间禁止断行 -->
他说—&#8288;—我不去了。
```
### 5. 国标依据
- GB/T 15834-2011 §4.10 / §5.1.4:破折号占两个字位置、上下居中、**不能中间断开**
## 八、Unicode 码位速查(W3C CLReq 推荐)
| 符号 | Unicode | 推荐 | 不推荐替代 |
|---|---|---|---|
| 句号 | U+3002 。 | ✅ | U+FF0E . / U+002E . |
| 逗号 | U+FF0C , | ✅ | U+002C , |
| 顿号 | U+3001 、 | ✅ | — |
| 分号 | U+FF1B ; | ✅ | U+003B ; |
| 冒号 | U+FF1A : | ✅ | U+003A : |
| 问号 | U+FF1F ? | ✅ | U+003F ? |
| 叹号 | U+FF01 ! | ✅ | U+0021 ! |
| 弯双引号开 | U+201C " | ✅(横排)| — |
| 弯双引号闭 | U+201D " | ✅(横排)| — |
| 直角引号开 | U+300C 「 | ✅(直排)| — |
| 直角引号闭 | U+300D 」 | ✅(直排)| — |
| 双重直角引号开 | U+300E 『 | ✅(直排)| — |
| 双重直角引号闭 | U+300F 』 | ✅(直排)| — |
| 圆括号开 | U+FF08 ( | ✅ | U+0028 ( |
| 圆括号闭 | U+FF09 ) | ✅ | U+0029 ) |
| 书名号开 | U+300A 《 | ✅ | — |
| 书名号闭 | U+300B 》 | ✅ | — |
| 单书名号开 | U+3008 〈 | ✅ | — |
| 单书名号闭 | U+3009 〉 | ✅ | — |
| 破折号 | U+2E3A ⸺ / U+2014×2 —— | ✅ | U+2015 / U+2500 / U+30FC |
| 省略号 | U+2026×2 …… | ✅ | U+22EF(横直自动居中替代)|
| 连接号短横 | U+2013 – | ✅ | — |
| 连接号一字线 | U+2014 — | ✅ | — |
| 浪纹线 | U+FF5E ~ | ✅ | U+007E ~ |
| 间隔号 | U+00B7 · | ✅ | U+30FB / U+2027 / U+2022 |
| 分隔号 | U+002F / 或 U+FF0F / | ✅ | — |
| 着重号 | U+25CF ● 或 U+2022 • | ✅ | — |
## 九、CSS 完整实现(现代浏览器,2024+)
```css
.body-text {
/* 字间距:密排 */
letter-spacing: 0;
/* 标点空间挤压(加法模式实现) */
text-spacing-trim: space-all;
/* 标点悬挂到版心外(行尾标点视觉对齐) */
hanging-punctuation: allow-end;
/* 严格避头尾 */
line-break: strict;
/* 不在词中断 */
word-break: keep-all;
/* 超长 URL 允许换行 */
overflow-wrap: break-word;
/* 两端对齐 */
text-align: justify;
/* 中文字间分布 */
text-justify: inter-character;
/* 标点宽度优化(OpenType) */
font-feature-settings:
"halt" 1, /* 半宽间距 */
"palt" 1; /* 成比例排(标题用) */
}
/* 标题:不要 halt,标题密排 */
h1, h2, h3 {
font-feature-settings: "palt" 1; /* 仅成比例排 */
}
/* 网页 / H5 / 卡片正文:通常不做首行缩进 */
p {
text-indent: 0;
margin: 0 0 1em 0;
}
/* 书籍 / 公文 / 古典长文如需首行缩进,应由对应 template 明确指定 */
.book-copy p,
.official-doc p,
.classical-copy p {
text-indent: 2em;
margin: 0;
}
/* 引文段:不要斜体;缩进/边线/底色由版式决定 */
blockquote {
font-style: normal;
}
```
旧浏览器降级:用 `<wbr>` 手工断行;用脚本处理(如 `kerning.js`)。
## 十、检查清单(标点相关)
排印完成前必过:
- [ ] 标点风格(全角 / 半角 / 开明 / 居中)是否全文统一?
- [ ] 行末全宽标点是否变为半宽?
- [ ] 避头尾级别(basic / GB / strict)是否选定并启用?
- [ ] 连续标点是否被挤压到 1.5 字宽内?
- [ ] 破折号是否用了 U+2014×2(不是 4 个连字符)?
- [ ] 破折号字体是否选了思源 / 苹方而不是中易 / 旧版雅黑?
- [ ] 破折号是否被加入"禁止分开"集?
- [ ] 引号横排用弯""'',直排用直角「」『』?
- [ ] CSS `font-family` 是否把中文字体放在英文字体之前?
- [ ] HTML 是否声明了 `lang="zh-CN"` 或 `lang="zh-TW"`?
- [ ] 段落组织是否符合媒介:网页/卡片不机械首行缩进,书籍/公文/古典长文才按模板缩进?
- [ ] 中英混排是否有 1/4 字宽间距?