feat: 汉字介绍待续
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,201 @@
|
||||
# 使用 1Panel + OpenResty 部署 pipi-assets
|
||||
|
||||
将 voice-production 的静态资源(voice、char、后续 image)通过 1Panel 安装的 OpenResty 对外提供,并绑定二级域名。OpenResty 与 Nginx 配置兼容,可直接使用本项目生成的配置。
|
||||
|
||||
---
|
||||
|
||||
## 一、前置条件
|
||||
|
||||
- 服务器已安装 [1Panel](https://1panel.cn/)
|
||||
- 已通过 1Panel **应用商店** 安装 **OpenResty**
|
||||
- 项目代码在服务器上(如 `/home/joey/sites/platform-pipi`),或能在本机执行部署脚本并同步到服务器
|
||||
|
||||
1Panel 默认安装目录为 `/opt/1panel`,下文中 OpenResty 相关路径均以此为准;若你的 1Panel 安装在其他目录,请替换为实际路径。
|
||||
|
||||
---
|
||||
|
||||
## 二、1Panel 中 OpenResty 的目录约定
|
||||
|
||||
OpenResty 在 1Panel 中以容器方式运行,宿主机与容器路径对应关系如下:
|
||||
|
||||
| 用途 | 宿主机路径(示例) | 容器内路径 |
|
||||
|--------------|--------------------|------------|
|
||||
| 站点配置片段 | `.../openresty/conf/conf.d/` | `/usr/local/openresty/nginx/conf/conf.d/` |
|
||||
| 网站根目录 | `.../openresty/www/` | `/www` |
|
||||
|
||||
完整宿主机路径(1Panel 默认):
|
||||
|
||||
- 配置目录:`/opt/1panel/apps/openresty/openresty/conf/conf.d/`
|
||||
- 网站目录:`/opt/1panel/apps/openresty/openresty/www/`
|
||||
|
||||
我们将把 pipi-assets 放在网站目录下,例如:`www/pipi-assets`,容器内访问为 `/www/pipi-assets`。
|
||||
|
||||
---
|
||||
|
||||
## 三、部署静态资源到 OpenResty 可访问的目录
|
||||
|
||||
### 3.1 确定目标目录
|
||||
|
||||
在**宿主机**上,使用 1Panel OpenResty 的网站目录下的子目录作为发布目录(请按你的 1Panel 安装路径调整):
|
||||
|
||||
```bash
|
||||
# 1Panel 默认安装目录为 /opt/1panel,若不同请修改
|
||||
OPENRESTY_WWW="/opt/1panel/apps/openresty/openresty/www"
|
||||
TARGET="${OPENRESTY_WWW}/pipi-assets"
|
||||
sudo mkdir -p "$TARGET"
|
||||
sudo chown -R $USER:$USER "$TARGET"
|
||||
```
|
||||
|
||||
记下 `TARGET` 的取值(下文中称为「pipi-assets 宿主机路径」),后续部署脚本会用到。
|
||||
|
||||
### 3.2 方式 A:在服务器上执行部署脚本(推荐)
|
||||
|
||||
项目已在服务器上时(如 `/home/joey/sites/platform-pipi`):
|
||||
|
||||
```bash
|
||||
cd /home/joey/sites/platform-pipi/packages/voice-production
|
||||
pnpm install
|
||||
# 将 data/ 拷贝到 OpenResty 的 www/pipi-assets
|
||||
node scripts/deploy-assets.js --target /opt/1panel/apps/openresty/openresty/www/pipi-assets
|
||||
```
|
||||
|
||||
请将 `--target` 改为你在 3.1 中使用的实际路径。
|
||||
|
||||
### 3.3 方式 B:本机生成后同步到服务器
|
||||
|
||||
在本机执行:
|
||||
|
||||
```bash
|
||||
cd platform-pipi/packages/voice-production
|
||||
pnpm run deploy-assets:local
|
||||
rsync -avz --delete ./pipi-assets/ 用户@服务器IP:/opt/1panel/apps/openresty/openresty/www/pipi-assets/
|
||||
```
|
||||
|
||||
若服务器上该目录需要 root,可先同步到用户目录,再 SSH 到服务器用 root 拷贝到上述路径。
|
||||
|
||||
---
|
||||
|
||||
## 四、添加 OpenResty 站点配置
|
||||
|
||||
OpenResty 与 Nginx 配置兼容。有两种方式添加 pipi-assets 的 server 配置。
|
||||
|
||||
### 4.1 方式一:在 1Panel 中创建网站(推荐)
|
||||
|
||||
1. 登录 1Panel → **网站** → 点击 **创建网站**。
|
||||
2. 选择 **静态网站**(或仅需“运行目录”的站点类型)。
|
||||
3. 填写:
|
||||
- **主域名**:你的二级域名(如 `assets.yourdomain.com`)。
|
||||
- **运行目录**:容器内路径,填 **`/www/pipi-assets`**(对应宿主机 `.../www/pipi-assets`)。
|
||||
- 若有「网站路径」「根目录」等,同样填 **`/www/pipi-assets`**。
|
||||
4. 创建完成后,在网站列表中找到该站点,进入 **配置** / **配置文件**,确认 `server` 块中:
|
||||
- `root` 或 `alias` 指向 **`/www/pipi-assets`**;
|
||||
- 若有 `location /`,可保持或按需增加下面「推荐 location」中的缓存与跨域头。
|
||||
|
||||
若 1Panel 生成的默认配置已能正确访问 `/www/pipi-assets` 下的文件,可不必改;否则可把下面 4.2 的配置内容合并进去。
|
||||
|
||||
### 4.2 方式二:手动添加 conf.d 配置片段
|
||||
|
||||
在**宿主机**上创建新的配置文件(路径按 1Panel 实际安装目录调整):
|
||||
|
||||
```bash
|
||||
sudo nano /opt/1panel/apps/openresty/openresty/conf/conf.d/pipi-assets.conf
|
||||
```
|
||||
|
||||
写入以下内容(将 `server_name` 改为你的二级域名):
|
||||
|
||||
```nginx
|
||||
# pipi-assets 静态资源(voice / char / image)
|
||||
server {
|
||||
listen 80;
|
||||
server_name assets.yourdomain.com;
|
||||
|
||||
root /www/pipi-assets;
|
||||
autoindex off;
|
||||
|
||||
location /voice/ {
|
||||
alias /www/pipi-assets/voice/;
|
||||
add_header Cache-Control "public, max-age=86400";
|
||||
}
|
||||
location /char/ {
|
||||
alias /www/pipi-assets/char/;
|
||||
add_header Cache-Control "public, max-age=3600";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
}
|
||||
location /image/ {
|
||||
alias /www/pipi-assets/image/;
|
||||
add_header Cache-Control "public, max-age=86400";
|
||||
}
|
||||
|
||||
location /health {
|
||||
return 200 "ok";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意:这里所有路径均为**容器内路径**(`/www/pipi-assets`),因为 OpenResty 在容器中运行。
|
||||
|
||||
---
|
||||
|
||||
## 五、重载 OpenResty
|
||||
|
||||
- **1Panel 界面**:网站 → OpenResty 工具栏 → 点击 **重载**,使新配置生效且不中断服务。
|
||||
- **命令行**(若你有权限进入容器或宿主机上能执行 1Panel 管理的 OpenResty):
|
||||
```bash
|
||||
# 仅当 1Panel 使用 systemd 管理 OpenResty 时可能可用,否则以 1Panel 重载为准
|
||||
# openresty -s reload
|
||||
```
|
||||
建议以 1Panel 的 **重载** 为准。
|
||||
|
||||
---
|
||||
|
||||
## 六、DNS 与 HTTPS
|
||||
|
||||
1. **DNS**:在域名服务商为二级域名(如 `assets.yourdomain.com`)添加 **A 记录**,指向当前服务器公网 IP。
|
||||
2. **HTTPS**:
|
||||
- 1Panel 网站列表中,进入该站点 → **SSL** / **证书**,选择 1Panel 提供的 **Let's Encrypt** 或上传自有证书。
|
||||
- 开启 HTTPS 后,OpenResty 会监听 443 并指向同一 `root`(/www/pipi-assets),无需改文档中的路径。
|
||||
|
||||
---
|
||||
|
||||
## 七、使用本项目生成的 Nginx 配置(可选)
|
||||
|
||||
本项目 `deploy-assets.js` 生成的配置默认是给「系统 Nginx」用的,`root` 为宿主机路径。给 OpenResty 用时需把 `root` 改为容器内路径:
|
||||
|
||||
- 生成配置时目标仍用宿主机路径(仅用于拷贝 data):
|
||||
```bash
|
||||
node scripts/deploy-assets.js --target /opt/1panel/apps/openresty/openresty/www/pipi-assets
|
||||
```
|
||||
- 打开生成的 `scripts/nginx-pipi-assets.conf`,将其中**所有** `root` / `alias` 的路径从「宿主机路径」改为 **`/www/pipi-assets`**(或 `/www/pipi-assets/voice/` 等),再复制到 `conf.d/pipi-assets.conf` 使用。
|
||||
|
||||
---
|
||||
|
||||
## 八、验证
|
||||
|
||||
- 健康检查:`http://你的二级域名/health` 或 `https://...` → 应返回 `ok`。
|
||||
- 角色 JSON:`http://你的二级域名/char/char_introduce.json` → 应返回 JSON。
|
||||
- 语音:`http://你的二级域名/voice/xxx.mp3` → 可播放或下载。
|
||||
|
||||
---
|
||||
|
||||
## 九、后续更新资源
|
||||
|
||||
- **方式 A**:在服务器项目目录执行
|
||||
`node scripts/deploy-assets.js --target /opt/1panel/apps/openresty/openresty/www/pipi-assets`
|
||||
覆盖更新 `pipi-assets` 目录内容。
|
||||
- **方式 B**:本机执行 `pnpm run deploy-assets:local` 后,用 rsync 同步到服务器上述 `pipi-assets` 路径。
|
||||
|
||||
更新后无需重启 OpenResty,直接刷新页面即可。
|
||||
|
||||
---
|
||||
|
||||
## 十、路径与注意事项小结
|
||||
|
||||
| 项目 | 说明 |
|
||||
|----------------|------|
|
||||
| 1Panel 安装目录 | 常见为 `/opt/1panel`,以实际为准。 |
|
||||
| 宿主机 pipi-assets | `.../openresty/openresty/www/pipi-assets`,部署脚本和 rsync 写这里。 |
|
||||
| 容器内 root | 配置里统一用 **`/www/pipi-assets`**。 |
|
||||
| 重载 | 在 1Panel 网站页对 OpenResty 使用 **重载**,不要直接改容器内文件。 |
|
||||
|
||||
若你使用非默认 1Panel 安装路径,将文中 `/opt/1panel` 全部替换为你的安装目录即可。
|
||||
@@ -0,0 +1,190 @@
|
||||
# voice-production 部署到 Ubuntu 云服务器
|
||||
|
||||
将 `voice-production` 的静态资源(voice、char、后续图片)部署到 Ubuntu,用 Nginx 提供访问并绑定二级域名。
|
||||
|
||||
---
|
||||
|
||||
## 一、服务器准备
|
||||
|
||||
### 1. 安装 Nginx
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install nginx -y
|
||||
sudo systemctl enable nginx
|
||||
sudo systemctl start nginx
|
||||
```
|
||||
|
||||
### 2. 安装 Node.js 与 pnpm(用于在服务器上执行部署脚本)
|
||||
|
||||
若你打算在服务器上跑 `deploy-assets` 和后续的生成脚本,需要 Node:
|
||||
|
||||
```bash
|
||||
# Node 20 LTS(推荐)
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
|
||||
# pnpm
|
||||
sudo npm install -g pnpm
|
||||
```
|
||||
|
||||
若你只打算从本机把已生成好的 `pipi-assets` 同步到服务器,可跳过 pnpm,但至少保留 Node 以便将来在服务器上跑脚本。
|
||||
|
||||
### 3. 创建资源目录(若用脚本部署会自动创建,也可先建)
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/pipi-assets
|
||||
sudo chown $USER:$USER /var/www/pipi-assets
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、部署方式(二选一)
|
||||
|
||||
### 方式 A:在服务器上拉代码并执行部署脚本(推荐)
|
||||
|
||||
适合:代码已在 Git 仓库,服务器能访问仓库。
|
||||
|
||||
**1. 在服务器上克隆/拉取项目**
|
||||
|
||||
```bash
|
||||
# 进入你放项目的目录,例如
|
||||
cd /var/www
|
||||
sudo git clone <你的仓库地址> pipi-learn
|
||||
cd pipi-learn/platform-pipi/packages/voice-production
|
||||
# 若已存在目录则只拉取
|
||||
# git pull
|
||||
```
|
||||
|
||||
**2. 安装依赖并执行部署**
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm run deploy-assets:server
|
||||
```
|
||||
|
||||
会把 `data/` 下内容拷贝到 `/var/www/pipi-assets/`,并在当前目录生成 `scripts/nginx-pipi-assets.conf`。
|
||||
|
||||
**3. 把 Nginx 配置放到系统目录并启用**
|
||||
|
||||
```bash
|
||||
# 若需改二级域名,先编辑 scripts/nginx-pipi-assets.conf 里的 server_name
|
||||
sudo cp scripts/nginx-pipi-assets.conf /etc/nginx/sites-available/pipi-assets
|
||||
sudo ln -sf /etc/nginx/sites-available/pipi-assets /etc/nginx/sites-enabled
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**4. 配置 DNS**
|
||||
|
||||
在域名服务商处为二级域名(如 `assets.yourdomain.com`)添加 **A 记录**,指向该云服务器的公网 IP。
|
||||
|
||||
**5.(可选)HTTPS**
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx -y
|
||||
sudo certbot --nginx -d assets.yourdomain.com
|
||||
```
|
||||
|
||||
按提示选择即可,证书会自动写入 Nginx 配置。
|
||||
|
||||
---
|
||||
|
||||
### 方式 B:本机生成后同步到服务器
|
||||
|
||||
适合:在本地生成 voice/char,只把静态文件上传到服务器。
|
||||
|
||||
**1. 本机生成并拷贝到“服务器目标结构”**
|
||||
|
||||
在 Mac 上:
|
||||
|
||||
```bash
|
||||
cd platform-pipi/packages/voice-production
|
||||
pnpm run deploy-assets:local # 生成 pipi-assets/
|
||||
```
|
||||
|
||||
**2. 把 pipi-assets 同步到服务器**
|
||||
|
||||
```bash
|
||||
rsync -avz --delete ./pipi-assets/ 用户名@服务器IP:/var/www/pipi-assets/
|
||||
```
|
||||
|
||||
若服务器上 `/var/www/pipi-assets` 需要 sudo,可先同步到用户目录再在服务器上移动:
|
||||
|
||||
```bash
|
||||
rsync -avz --delete ./pipi-assets/ 用户名@服务器IP:~/pipi-assets/
|
||||
# 登录服务器后
|
||||
ssh 用户名@服务器IP
|
||||
sudo rm -rf /var/www/pipi-assets/* && sudo cp -r ~/pipi-assets/* /var/www/pipi-assets/
|
||||
```
|
||||
|
||||
**3. 在服务器上准备 Nginx 配置**
|
||||
|
||||
- 要么在服务器上拉取一遍 voice-production 代码,执行一次 `pnpm run deploy-assets:server`,只为了生成 `scripts/nginx-pipi-assets.conf`,再按方式 A 第 3 步拷贝并启用;
|
||||
- 要么在本机生成配置后上传。本机执行:
|
||||
|
||||
```bash
|
||||
node scripts/deploy-assets.js --target /var/www/pipi-assets --nginx ./scripts/nginx-pipi-assets.conf
|
||||
```
|
||||
|
||||
把生成的 `scripts/nginx-pipi-assets.conf` 用 scp 传到服务器,再在服务器上:
|
||||
|
||||
```bash
|
||||
sudo cp nginx-pipi-assets.conf /etc/nginx/sites-available/pipi-assets
|
||||
sudo ln -sf /etc/nginx/sites-available/pipi-assets /etc/nginx/sites-enabled
|
||||
```
|
||||
|
||||
**4. 修改配置里的 server_name(若尚未改)**
|
||||
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/pipi-assets
|
||||
# 将 server_name assets.yourdomain.com; 改为你的二级域名
|
||||
```
|
||||
|
||||
**5. 测试并重载 Nginx**
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
DNS 与 HTTPS 步骤同方式 A 的 4、5。
|
||||
|
||||
---
|
||||
|
||||
## 三、修改二级域名
|
||||
|
||||
生成的 `nginx-pipi-assets.conf` 里默认是 `server_name assets.yourdomain.com;`。
|
||||
|
||||
- **方式 A**:在服务器上编辑 `scripts/nginx-pipi-assets.conf` 再拷贝到 `/etc/nginx/sites-available/`,或直接编辑 `/etc/nginx/sites-available/pipi-assets`。
|
||||
- **方式 B**:本机生成配置后编辑再上传,或在服务器上编辑 `/etc/nginx/sites-available/pipi-assets`。
|
||||
|
||||
改完后执行:
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、后续更新资源
|
||||
|
||||
- **方式 A**:在服务器上进入 `voice-production` 目录,执行 `git pull`,再执行 `pnpm run deploy-assets:server`;若在服务器上跑生成脚本,先 `pnpm run generate-voice` 等再执行 `deploy-assets:server`。
|
||||
- **方式 B**:本机重新生成/拷贝后,再执行一次 `rsync` 同步到服务器。
|
||||
|
||||
---
|
||||
|
||||
## 五、常用命令速查
|
||||
|
||||
| 说明 | 命令 |
|
||||
| --------------- | ---------------------------------------- |
|
||||
| 测试 Nginx 配置 | `sudo nginx -t` |
|
||||
| 重载 Nginx | `sudo systemctl reload nginx` |
|
||||
| 查看 Nginx 状态 | `sudo systemctl status nginx` |
|
||||
| 查看站点日志 | `sudo tail -f /var/log/nginx/access.log` |
|
||||
|
||||
---
|
||||
|
||||
## 六、验证
|
||||
|
||||
- 健康检查:`https://你的二级域名/health` 应返回 `ok`。
|
||||
- 角色 JSON:`https://你的二级域名/char/char_introduce.json` 应返回 JSON。
|
||||
- 语音:`https://你的二级域名/voice/xxx.mp3` 应能播放或下载。
|
||||
@@ -0,0 +1,33 @@
|
||||
const fs = require('fs');
|
||||
|
||||
// Read char_common_base.json (each line is a JSON object)
|
||||
const basePath = '/Users/joey-xd/sites/pipi-learn/platform-pipi/apps/api/src/data/char_common_base.json';
|
||||
const baseText = fs.readFileSync(basePath, 'utf8').trim();
|
||||
const baseLines = baseText.split('\n').filter(l => l.trim());
|
||||
const chars = baseLines.map(l => {
|
||||
let cleaned = l.trim().replace(/,$/, '');
|
||||
return JSON.parse(cleaned);
|
||||
});
|
||||
|
||||
console.log(`总字数: ${chars.length}`);
|
||||
|
||||
const singlePinyin = chars.filter(c => c.pinyin.length === 1);
|
||||
const multiPinyin = chars.filter(c => c.pinyin.length > 1);
|
||||
|
||||
console.log(`单音字: ${singlePinyin.length}`);
|
||||
console.log(`多音字: ${multiPinyin.length}`);
|
||||
|
||||
// Count by number of pinyins
|
||||
const byCount = {};
|
||||
multiPinyin.forEach(c => {
|
||||
const n = c.pinyin.length;
|
||||
if (!byCount[n]) byCount[n] = [];
|
||||
byCount[n].push(c);
|
||||
});
|
||||
|
||||
Object.keys(byCount).sort().forEach(n => {
|
||||
console.log(`\n=== ${n} 个读音 (${byCount[n].length} 字) ===`);
|
||||
byCount[n].forEach(c => {
|
||||
console.log(` ${c.char}: [${c.pinyin.join(', ')}]`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,420 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const inputPath =
|
||||
'/Users/joey-xd/sites/pipi-learn/platform-pipi/packages/voice-production/data/char/char_introduce_v2.json';
|
||||
const data = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
|
||||
|
||||
// ========== 修复词典 ==========
|
||||
// 格式: char → 常用词/短语,用于生成 "char,word的char"
|
||||
// 覆盖 "X字的X" 和 "X的X" 两类问题
|
||||
const wordFixes = {
|
||||
// --- "X字的X" 类 (294个) ---
|
||||
迟: '迟到',
|
||||
局: '局面',
|
||||
改: '改变',
|
||||
张: '张开',
|
||||
忌: '禁忌',
|
||||
际: '国际',
|
||||
陆: '陆地',
|
||||
阿: '阿谀',
|
||||
陈: '陈列',
|
||||
阻: '阻止',
|
||||
附: '附近',
|
||||
坠: '坠落',
|
||||
妓: '歌妓',
|
||||
妙: '美妙',
|
||||
妖: '妖怪',
|
||||
姊: '姊妹',
|
||||
妨: '妨碍',
|
||||
妒: '嫉妒',
|
||||
努: '努力',
|
||||
忍: '忍耐',
|
||||
抽: '抽屉',
|
||||
拐: '拐弯',
|
||||
拖: '拖延',
|
||||
者: '读者',
|
||||
拍: '拍手',
|
||||
顶: '头顶',
|
||||
拆: '拆除',
|
||||
拎: '拎包',
|
||||
拥: '拥抱',
|
||||
抵: '抵达',
|
||||
拘: '拘留',
|
||||
势: '势力',
|
||||
抱: '拥抱',
|
||||
拄: '拄杖',
|
||||
垃: '垃圾',
|
||||
拉: '拉开',
|
||||
拦: '拦截',
|
||||
幸: '幸福',
|
||||
拌: '搅拌',
|
||||
拧: '拧开',
|
||||
歧: '分歧',
|
||||
肯: '肯定',
|
||||
齿: '牙齿',
|
||||
些: '一些',
|
||||
卓: '卓越',
|
||||
虎: '老虎',
|
||||
虏: '俘虏',
|
||||
肾: '肾脏',
|
||||
贤: '贤明',
|
||||
尚: '高尚',
|
||||
旺: '兴旺',
|
||||
具: '工具',
|
||||
味: '味道',
|
||||
果: '水果',
|
||||
昆: '昆虫',
|
||||
国: '国家',
|
||||
哎: '哎呀',
|
||||
咕: '咕噜',
|
||||
昌: '昌盛',
|
||||
呵: '呵护',
|
||||
疙: '疙瘩',
|
||||
柑: '柑橘',
|
||||
枯: '枯萎',
|
||||
柄: '把柄',
|
||||
栋: '栋梁',
|
||||
相: '互相',
|
||||
查: '检查',
|
||||
柏: '柏树',
|
||||
栅: '栅栏',
|
||||
柳: '柳树',
|
||||
柱: '柱子',
|
||||
柿: '柿子',
|
||||
栏: '栏杆',
|
||||
柠: '柠檬',
|
||||
树: '大树',
|
||||
勃: '蓬勃',
|
||||
要: '需要',
|
||||
柬: '请柬',
|
||||
咸: '咸淡',
|
||||
威: '威力',
|
||||
歪: '歪斜',
|
||||
韭: '韭菜',
|
||||
背: '背后',
|
||||
战: '战争',
|
||||
点: '点头',
|
||||
虐: '虐待',
|
||||
临: '临时',
|
||||
览: '浏览',
|
||||
竖: '竖立',
|
||||
省: '省份',
|
||||
削: '削苹果',
|
||||
尝: '品尝',
|
||||
昧: '愚昧',
|
||||
盹: '打盹',
|
||||
是: '是非',
|
||||
盼: '盼望',
|
||||
眨: '眨眼',
|
||||
哇: '哇塞',
|
||||
哄: '哄堂大笑',
|
||||
哑: '哑巴',
|
||||
显: '显然',
|
||||
很: '很好',
|
||||
猎: '打猎',
|
||||
猫: '猫咪',
|
||||
凰: '凤凰',
|
||||
猖: '猖狂',
|
||||
猛: '猛烈',
|
||||
祭: '祭祀',
|
||||
馅: '馅饼',
|
||||
馆: '博物馆',
|
||||
凑: '凑合',
|
||||
减: '减少',
|
||||
毫: '毫米',
|
||||
烹: '烹饪',
|
||||
庶: '庶民',
|
||||
麻: '麻烦',
|
||||
庵: '尼姑庵',
|
||||
痊: '痊愈',
|
||||
痒: '发痒',
|
||||
痕: '痕迹',
|
||||
廊: '走廊',
|
||||
康: '健康',
|
||||
谓: '所谓',
|
||||
谚: '谚语',
|
||||
谜: '谜语',
|
||||
逮: '逮捕',
|
||||
敢: '勇敢',
|
||||
尉: '上尉',
|
||||
屠: '屠宰',
|
||||
弹: '子弹',
|
||||
隋: '隋朝',
|
||||
堕: '堕落',
|
||||
随: '随便',
|
||||
蛋: '鸡蛋',
|
||||
隅: '角隅',
|
||||
隆: '隆重',
|
||||
隐: '隐藏',
|
||||
婚: '结婚',
|
||||
婶: '婶婶',
|
||||
婉: '婉转',
|
||||
颇: '颇多',
|
||||
颈: '颈椎',
|
||||
替: '替代',
|
||||
揍: '揍人',
|
||||
款: '款式',
|
||||
堪: '堪称',
|
||||
塔: '宝塔',
|
||||
搭: '搭建',
|
||||
堰: '堤堰',
|
||||
揩: '揩油',
|
||||
越: '超越',
|
||||
趁: '趁机',
|
||||
趋: '趋势',
|
||||
超: '超级',
|
||||
揽: '包揽',
|
||||
堤: '堤坝',
|
||||
提: '提高',
|
||||
博: '博学',
|
||||
揭: '揭开',
|
||||
喜: '欢喜',
|
||||
彭: '彭姓',
|
||||
揣: '揣测',
|
||||
装: '装扮',
|
||||
蛮: '野蛮',
|
||||
就: '成就',
|
||||
敦: '敦厚',
|
||||
斌: '文武双全',
|
||||
痘: '痘痘',
|
||||
痢: '痢疾',
|
||||
痪: '瘫痪',
|
||||
痛: '痛苦',
|
||||
童: '儿童',
|
||||
竣: '竣工',
|
||||
阔: '辽阔',
|
||||
善: '善良',
|
||||
翔: '飞翔',
|
||||
羡: '羡慕',
|
||||
普: '普通',
|
||||
粪: '粪便',
|
||||
尊: '尊重',
|
||||
奠: '奠基',
|
||||
道: '道路',
|
||||
榄: '橄榄',
|
||||
想: '想象',
|
||||
槐: '槐树',
|
||||
榆: '榆树',
|
||||
楼: '楼房',
|
||||
概: '大概',
|
||||
赖: '依赖',
|
||||
酬: '报酬',
|
||||
感: '感觉',
|
||||
碍: '阻碍',
|
||||
尴: '尴尬',
|
||||
矮: '矮小',
|
||||
辞: '辞职',
|
||||
稚: '幼稚',
|
||||
稠: '稠密',
|
||||
颓: '颓废',
|
||||
愁: '忧愁',
|
||||
筹: '筹划',
|
||||
简: '简单',
|
||||
筷: '筷子',
|
||||
毁: '毁灭',
|
||||
舅: '舅舅',
|
||||
鼠: '老鼠',
|
||||
催: '催促',
|
||||
傻: '傻瓜',
|
||||
像: '好像',
|
||||
躲: '躲藏',
|
||||
魁: '魁梧',
|
||||
衙: '衙门',
|
||||
微: '微小',
|
||||
馍: '馍馍',
|
||||
叠: '重叠',
|
||||
缠: '缠绕',
|
||||
缤: '缤纷',
|
||||
剿: '围剿',
|
||||
静: '安静',
|
||||
碧: '碧绿',
|
||||
璃: '玻璃',
|
||||
赘: '累赘',
|
||||
熬: '熬夜',
|
||||
墙: '围墙',
|
||||
墟: '废墟',
|
||||
嘉: '嘉奖',
|
||||
摧: '摧毁',
|
||||
赫: '显赫',
|
||||
截: '截断',
|
||||
誓: '发誓',
|
||||
境: '环境',
|
||||
摘: '采摘',
|
||||
摔: '摔倒',
|
||||
撇: '撇开',
|
||||
聚: '聚集',
|
||||
蜡: '蜡烛',
|
||||
蝇: '苍蝇',
|
||||
蜘: '蜘蛛',
|
||||
蝉: '蝉鸣',
|
||||
嘛: '干嘛',
|
||||
嘀: '嘀咕',
|
||||
赚: '赚钱',
|
||||
锹: '铁锹',
|
||||
锻: '锻炼',
|
||||
镀: '镀金',
|
||||
舞: '跳舞',
|
||||
舔: '舔嘴',
|
||||
稳: '稳定',
|
||||
熏: '熏陶',
|
||||
箕: '簸箕',
|
||||
算: '计算',
|
||||
箩: '箩筐',
|
||||
管: '管理',
|
||||
箫: '洞箫',
|
||||
舆: '舆论',
|
||||
薄: '薄饼',
|
||||
颠: '颠倒',
|
||||
翰: '翰墨',
|
||||
噩: '噩梦',
|
||||
橱: '橱柜',
|
||||
橙: '橙子',
|
||||
橘: '橘子',
|
||||
整: '整齐',
|
||||
融: '融化',
|
||||
瓢: '瓢虫',
|
||||
醒: '清醒',
|
||||
霍: '霍然',
|
||||
霎: '霎时',
|
||||
辙: '车辙',
|
||||
冀: '希冀',
|
||||
餐: '餐厅',
|
||||
嘴: '嘴巴',
|
||||
踱: '踱步',
|
||||
蹄: '马蹄',
|
||||
蹂: '蹂躏',
|
||||
靡: '奢靡',
|
||||
癣: '皮癣',
|
||||
瓣: '花瓣',
|
||||
羹: '肉羹',
|
||||
鳖: '甲鳖',
|
||||
爆: '爆炸',
|
||||
疆: '边疆',
|
||||
鬓: '鬓角',
|
||||
壤: '土壤',
|
||||
馨: '温馨',
|
||||
耀: '闪耀',
|
||||
躁: '急躁',
|
||||
蠕: '蠕动',
|
||||
嚼: '咀嚼',
|
||||
嚷: '叫嚷',
|
||||
巍: '巍峨',
|
||||
籍: '书籍',
|
||||
鳞: '鱼鳞',
|
||||
魔: '魔法',
|
||||
糯: '糯米',
|
||||
|
||||
// --- "X的X" 类 (47个) ---
|
||||
你: '你好',
|
||||
系: '关系',
|
||||
茎: '茎叶',
|
||||
画: '画画',
|
||||
鱼: '鲤鱼',
|
||||
狗: '小狗',
|
||||
底: '底部',
|
||||
油: '石油',
|
||||
泡: '泡泡',
|
||||
空: '天空',
|
||||
钠: '钠元素',
|
||||
看: '看见',
|
||||
剑: '宝剑',
|
||||
总: '总是',
|
||||
给: '给予',
|
||||
起: '起来',
|
||||
根: '树根',
|
||||
党: '党员',
|
||||
哦: '哦哦',
|
||||
圆: '圆形',
|
||||
钾: '钾元素',
|
||||
铁: '铁路',
|
||||
铅: '铅笔',
|
||||
秤: '秤杆',
|
||||
般: '一般',
|
||||
爱: '爱心',
|
||||
狼: '灰狼',
|
||||
谁: '谁人',
|
||||
球: '足球',
|
||||
硅: '硅谷',
|
||||
做: '做事',
|
||||
您: '您好',
|
||||
假: '真假',
|
||||
率: '效率',
|
||||
锅: '铁锅',
|
||||
锌: '锌元素',
|
||||
鹅: '白鹅',
|
||||
然: '自然',
|
||||
粥: '稀粥',
|
||||
鼓: '打鼓',
|
||||
搬: '搬家',
|
||||
摇: '摇晃',
|
||||
搞: '搞笑',
|
||||
塘: '池塘',
|
||||
摊: '摊贩',
|
||||
聘: '聘请',
|
||||
慢: '慢慢',
|
||||
};
|
||||
|
||||
// ========== 修复逻辑 ==========
|
||||
let fixCount = 0;
|
||||
|
||||
function fixIntroduce(ch, intro) {
|
||||
// 检测 "X,X字的X" 模式
|
||||
const m1 = intro.match(/^(.),(.)字的\1$/);
|
||||
if (m1 && m1[1] === m1[2]) {
|
||||
const okPairs = ['文字', '写字', '名字', '签字', '数字'];
|
||||
if (!okPairs.includes(m1[2] + '字')) {
|
||||
if (wordFixes[ch]) {
|
||||
fixCount++;
|
||||
return `${ch},${wordFixes[ch]}的${ch}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 检测 "X,X的X" 自引用模式
|
||||
const m2 = intro.match(/^(.),\1的\1$/);
|
||||
if (m2) {
|
||||
if (wordFixes[ch]) {
|
||||
fixCount++;
|
||||
return `${ch},${wordFixes[ch]}的${ch}`;
|
||||
}
|
||||
}
|
||||
return intro;
|
||||
}
|
||||
|
||||
Object.entries(data).forEach(([ch, val]) => {
|
||||
if (ch.startsWith('_')) return;
|
||||
|
||||
if (typeof val[0] === 'string') {
|
||||
// 单音字: [pinyin, meaning, introduce]
|
||||
val[2] = fixIntroduce(ch, val[2]);
|
||||
} else {
|
||||
// 多音字: [[pinyin, meaning, introduce], ...]
|
||||
val.forEach((r) => {
|
||||
r[2] = fixIntroduce(ch, r[2]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 写入 ==========
|
||||
fs.writeFileSync(inputPath, JSON.stringify(data, null, 2), 'utf8');
|
||||
console.log(`修复完成: ${fixCount} 个 introduce 已更新`);
|
||||
|
||||
// ========== 验证 ==========
|
||||
const dataCheck = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
|
||||
let remaining = 0;
|
||||
Object.entries(dataCheck).forEach(([ch, val]) => {
|
||||
if (ch.startsWith('_')) return;
|
||||
function check(intro) {
|
||||
const m1 = intro.match(/^(.),(.)字的\1$/);
|
||||
if (
|
||||
m1 &&
|
||||
m1[1] === m1[2] &&
|
||||
!['文字', '写字', '名字', '签字', '数字'].includes(m1[2] + '字')
|
||||
)
|
||||
remaining++;
|
||||
const m2 = intro.match(/^(.),\1的\1$/);
|
||||
if (m2) remaining++;
|
||||
}
|
||||
if (typeof val[0] === 'string') check(val[2]);
|
||||
else val.forEach((r) => check(r[2]));
|
||||
});
|
||||
console.log(`剩余问题: ${remaining}`);
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user