🔄 卡若AI 同步 2026-02-23 23:22 | 更新:总索引与入口、水桥平台对接、卡土、运营中枢工作台 | 排除 >20MB: 10 个

This commit is contained in:
2026-02-23 23:22:06 +08:00
parent 741ac17d06
commit 0339d66097
18 changed files with 1154 additions and 8 deletions

2
.gitignore vendored
View File

@@ -18,6 +18,8 @@ __pycache__/
.env.*
*.log
sync_tokens.env
# QQ 邮箱授权码(勿提交)
**/QQ邮箱拉取/.qq_mail_env
# 飞书妙记(用户 token / Cookie勿提交
**/智能纪要/脚本/feishu_user_token.txt
**/智能纪要/脚本/cookie_minutes.txt

View File

@@ -0,0 +1,121 @@
---
name: MCP 搜索与连接
description: 当卡若AI需要连接 MCP 时,使用本技能搜索、发现并安装 MCP 服务器。触发词MCP、找MCP、连接MCP、MCP搜索、发现MCP、添加MCP、需要MCP、MCP安装、MCP发现。
owner: 水桥
group: 水
version: "1.0"
updated: "2026-02-23"
---
# MCP 搜索与连接 Skill
> 需要啥 MCP搜一搜、连一连。 —— 水桥
---
## 核心能力
**当卡若AI遇到需要 MCP 能力的场景时,使用本技能完成:**
1. 搜索 MCP 服务器5000+ 可发现)
2. 获取安装配置
3. 写入 Cursor/Claude/Windsurf 等客户端配置
4. 按需使用相应 MCP 工具
---
## 推荐工具MCPfinder
**MCPfinder** 是「MCP 的 App Store」—— 一次安装AI 自主发现并安装 MCP 服务器。
| 能力 | 说明 |
|:---|:---|
| 搜索 | 5000+ 服务器多注册表聚合Official、Glama、Smithery |
| 安装 | 一键生成 Cursor / Claude Desktop / Windsurf 等配置 |
| 排名 | 按相关性、热度、多注册表覆盖、更新 recency 排序 |
### 安装Cursor
`~/.cursor/mcp.json` 或项目 `.cursor/mcp.json` 中加入:
```json
{
"mcpServers": {
"mcpfinder": {
"command": "npx",
"args": ["-y", "@mcpfinder/server@beta"]
}
}
}
```
> 注:首次运行会同步注册表(约 12 分钟),之后本地缓存自动刷新。
### MCPfinder 工具一览
| 工具 | 用途 | 触发场景 |
|:---|:---|:---|
| `search_mcp_servers` | 按关键词/用例/技术搜索 | 用户需要你当前没有的能力 |
| `get_server_details` | 获取详情描述、env、来源等 | 评估是否适合安装 |
| `get_install_command` | 生成可直接粘贴的配置 | 用户要安装某个 MCP |
| `list_categories` | 按分类浏览 | 用户不确定需要啥 |
| `browse_category` | 查看某分类下的热门服务器 | 探索某领域数据库、AI、云等 |
---
## 执行流程卡若AI 使用本技能时)
```
用户需求(需要某种 MCP 能力)
① 判断:当前 MCP 工具是否已有该能力?
├── 有 → 直接调用对应 MCP 工具
└── 无 → 进入 ②
② 调用 MCPfinder.search_mcp_servers(query) 搜索
③ 若有结果,选 13 个候选,调用 get_server_details 评估
④ 调用 get_install_command 生成 Cursor 配置
⑤ 写入 ~/.cursor/mcp.json或项目 mcp.json
⑥ 提示用户重启 Cursor 或等待自动检测
⑦ 配置生效后,使用新 MCP 完成用户需求
```
---
## 其他 MCP 发现资源(备用)
当 MCPfinder 未安装或搜索无果时,可引导用户到:
| 资源 | 链接 | 说明 |
|:---|:---|:---|
| GitHub MCP Registry | https://github.com/mcp | 官方精选VS Code 一键安装 |
| MCP Awesome | https://mcp-awesome.com | 1200+ 经核验服务器 |
| Find My MCP | https://findmymcp.com | 可搜索目录 |
| Cursor Directory | https://cursor.directory/mcp | Cursor 专用目录 |
| awesome-mcp-servers | https://github.com/appcypher/awesome-mcp-servers | GitHub 5k+ star 列表 |
---
## 与 Skill 的配合
- **需要功能时**:本技能负责「找 MCP、连 MCP」
- **功能落地时**:按 `SKILL_REGISTRY.md` 匹配现有 Skill或结合「技能工厂」为新 MCP 写封装 Skill
- **MCP 安装后**:该 MCP 工具即成为卡若AI 的扩展能力,可直接在对话中调用
---
## 触发词
`MCP``找MCP``连接MCP``MCP搜索``发现MCP``添加MCP``需要MCP``MCP安装``MCP发现``查MCP``装MCP`
---
## 引用
- MCPfinder 新仓库https://github.com/lksrz/mcpfinder
- MCPfinder 官网https://mcpfinder.dev · https://findmcp.dev
- npm 包:`@mcpfinder/server`beta

View File

@@ -0,0 +1,32 @@
# QQ 邮箱 IMAP 拉取
> 水桥 · 平台对接 | 命令行接收 QQ 邮件
## 配置说明
- **授权码**:已保存在 `.qq_mail_env`(本地,不提交 git
- **账号**zhengzhiqun@qq.com
- **用途**:脚本启动时自动读取 `.qq_mail_env`,无需再手动设置环境变量
## 命令行用法
```bash
# 拉取最近 30 天(默认)
python qq_mail_fetch.py
# 拉取最近 7 天
python qq_mail_fetch.py --days 7
# 最多拉取 50 封
python qq_mail_fetch.py --limit 50
# 组合:最近 7 天、最多 20 封
python qq_mail_fetch.py --days 7 --limit 20
```
## 登录失败排查
若出现 `Login fail`,请检查:
1. QQ 邮箱 → 设置 → 账户 → POP3/IMAP/SMTP → **已开启 IMAP**
2. 授权码为刚生成的可再试一次(有时需等待数分钟生效)
3. 更换 QQ 密码会使授权码失效,需重新生成

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""
QQ 邮箱导出数据分析 · 生成整体总结报告
"""
import json
import re
from pathlib import Path
from collections import Counter
from datetime import datetime
def analyze(json_path: str) -> dict:
with open(json_path, "r", encoding="utf-8") as f:
emails = json.load(f)
if not emails:
return {"total": 0, "error": "无邮件数据"}
# 基础统计
total = len(emails)
dates = []
senders = []
subject_keywords = []
for e in emails:
d = e.get("date", "")[:10]
if d and len(d) >= 10:
dates.append(d)
from_addr = e.get("from", "")
if "<" in from_addr:
m = re.search(r"[\w.-]+@[\w.-]+", from_addr)
sender = m.group(0) if m else from_addr[:50]
else:
sender = from_addr[:50] if from_addr else "unknown"
senders.append(sender)
subj = e.get("subject", "")
# 提取发件域名/服务
if "github" in sender.lower():
if "Run failed" in subj or "Sync" in subj:
subject_keywords.append("GitHub_同步失败")
elif "security" in subj.lower():
subject_keywords.append("GitHub_安全告警")
else:
subject_keywords.append("GitHub_其他")
elif "synology" in sender.lower():
subject_keywords.append("Synology_NAS")
elif "vercel" in sender.lower():
subject_keywords.append("Vercel_部署")
elif "ollama" in sender.lower():
subject_keywords.append("Ollama_验证")
elif "apple" in sender.lower() or "icloud" in sender.lower():
subject_keywords.append("Apple_iCloud")
elif "trip.com" in sender.lower():
subject_keywords.append("Trip_推广")
elif "facebook" in sender.lower():
subject_keywords.append("Facebook_通知")
elif "adobe" in sender.lower():
subject_keywords.append("Adobe_推广")
elif "docker" in sender.lower():
subject_keywords.append("Docker_推广")
elif "airbnb" in sender.lower():
subject_keywords.append("Airbnb_通知")
elif "cebbank" in sender.lower() or "95595" in sender:
subject_keywords.append("光大银行")
elif "bosszhipin" in sender.lower():
subject_keywords.append("Boss直聘")
elif "openrouter" in sender.lower():
subject_keywords.append("OpenRouter_AI")
else:
subject_keywords.append("其他")
# 按发件人统计
sender_counts = Counter(senders)
top_senders = sender_counts.most_common(15)
# 按类型统计
type_counts = Counter(subject_keywords)
top_types = type_counts.most_common(15)
# 日期范围
dates_ok = [d for d in dates if re.match(r"\d{4}-\d{2}-\d{2}", d)]
date_min = min(dates_ok) if dates_ok else ""
date_max = max(dates_ok) if dates_ok else ""
return {
"total": total,
"date_range": {"min": date_min, "max": date_max},
"top_senders": top_senders,
"top_types": top_types,
"by_type": dict(type_counts),
}
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("json", nargs="?", default="/Users/karuo/Documents/卡若Ai的文件夹/报告/qq_mail_full_export.json")
ap.add_argument("-o", "--output", help="输出报告路径")
args = ap.parse_args()
r = analyze(args.json)
lines = [
"# QQ 邮箱整体分析报告",
"",
"## 一、概览",
"",
f"- 邮件总数:**{r['total']}** 封",
f"- 时间范围:{r['date_range']['min']} {r['date_range']['max']}",
"",
"## 二、按发件人统计Top 15",
"",
"| 发件人 | 数量 |",
"|:---|:---|",
]
for s, c in r["top_senders"]:
lines.append(f"| {s[:60]} | {c} |")
lines.extend([
"",
"## 三、按内容类型统计",
"",
"| 类型 | 数量 | 占比 |",
"|:---|:---|:---|",
])
for t, c in r["top_types"]:
pct = round(c / r["total"] * 100, 1) if r["total"] else 0
lines.append(f"| {t} | {c} | {pct}% |")
# 四、核心发现
lines.extend([
"",
"## 四、核心发现",
"",
])
gh_fail = r["by_type"].get("GitHub_同步失败", 0)
syno = r["by_type"].get("Synology_NAS", 0)
vercel = r["by_type"].get("Vercel_部署", 0)
boss = r["by_type"].get("Boss直聘", 0)
if gh_fail:
lines.append(f"- **GitHub 同步告警占 {round(gh_fail/r['total']*100,1)}%**cunkebao_doc 的 Coding 同步长期失败,建议修复或停用工作流")
if syno:
lines.append(f"- **Synology NAS 通知 {syno} 封**:容器异常、连接断连频繁,需排查 nas-frpc、mongodb 等")
if vercel:
lines.append(f"- **Vercel 部署失败 {vercel} 封**:部署权限或集成问题待查")
if boss:
lines.append(f"- **Boss直聘 {boss} 封**:招聘/求职相关")
lines.extend([
"",
"## 五、建议行动",
"",
"1. 优先处理 cunkebao_doc 同步失败,减少告警噪音",
"2. 排查 Synology 容器稳定性与 lkdie 连接",
"3. 检查 Vercel 与 GitHub 集成",
"4. 按需归档或过滤推广类邮件Trip、Facebook、Adobe 等)",
"",
"## 六、数据说明",
"",
"- 数据来源IMAP 收件箱INBOX全量拉取",
"- 网页版「我的文件夹」等需在 QQ 邮箱设置中勾选「收取我的文件夹」后,方能在 IMAP 中访问",
"- 导出文件:`qq_mail_full_export.json`",
"",
])
text = "\n".join(lines)
if args.output:
Path(args.output).write_text(text, encoding="utf-8")
print(f"已写入 {args.output}")
else:
print(text)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
QQ 邮箱 IMAP 拉取脚本 · 水桥(平台对接)
用途:拉取收件箱全部或指定时间范围的邮件,输出为可读格式
前置QQ 邮箱设置 → 账户 → POP3/IMAP/SMTP → 开启 IMAP → 生成授权码
用法:
python qq_mail_fetch.py # 拉取最近 30 天
python qq_mail_fetch.py --days 7 # 拉取最近 7 天
python qq_mail_fetch.py --all # 拉取全部历史邮件(无日期限制)
python qq_mail_fetch.py --all --output out.json # 导出到 JSON 便于分析
"""
import imaplib
import email
import argparse
import json
import os
import sys
from email.utils import parsedate_to_datetime
from datetime import datetime, timedelta
from pathlib import Path
# 加载本地 .qq_mail_env授权码保存于此调用时直接使用
def _load_env():
env_file = Path(__file__).resolve().parent / ".qq_mail_env"
if env_file.exists():
for line in env_file.read_text(encoding="utf-8").splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, v = line.split("=", 1)
os.environ.setdefault(k.strip(), v.strip())
_load_env()
# 配置:优先环境变量,次之 .qq_mail_env
IMAP_HOST = "imap.qq.com"
IMAP_PORT = 993
EMAIL = os.environ.get("QQ_MAIL", "zhengzhiqun@qq.com")
AUTH_CODE = os.environ.get("QQ_MAIL_AUTH_CODE", "") # 授权码,非 QQ 密码
def list_folders() -> list[str]:
"""列出所有 IMAP 文件夹"""
if not AUTH_CODE:
return []
server = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
server.login(EMAIL, AUTH_CODE)
typ, data = server.list()
server.logout()
if typ != "OK":
return []
folders = []
for line in data:
if line:
parts = line.decode().split('"')
if len(parts) >= 2:
folders.append(parts[-2])
return folders
def fetch_emails(days: int = 30, limit: int = 0, all_mail: bool = False, progress: bool = True, folder: str = "INBOX") -> list[dict]:
"""拉取收件箱邮件,返回 [{date, from, subject, preview}, ...]"""
if not AUTH_CODE:
print("请设置环境变量 QQ_MAIL_AUTH_CODEQQ 邮箱授权码)")
print("或在脚本内填写 AUTH_CODE 变量")
return []
server = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
server.login(EMAIL, AUTH_CODE)
# 含空格的文件夹名需加双引号(如 "Sent Messages"
mb = f'"{folder}"' if " " in folder else folder
try:
server.select(mb, readonly=True)
except (imaplib.IMAP4.error, imaplib.IMAP4.readonly):
server.select(mb, readonly=False)
if all_mail:
typ, data = server.search(None, "ALL")
else:
since = (datetime.now() - timedelta(days=days)).strftime("%d-%b-%Y")
typ, data = server.search(None, f"(SINCE {since})")
if typ != "OK":
server.close()
server.logout()
return []
ids = data[0].split()
ids = list(reversed(ids)) # 新的在前
total = len(ids)
if limit and total > limit:
ids = ids[:limit]
total_fetch = len(ids)
if progress:
print(f"{total} 封邮件,将拉取 {total_fetch} 封...", file=sys.stderr)
results = []
for i, num in enumerate(ids):
typ, msg_data = server.fetch(num, "(RFC822)")
if typ != "OK":
continue
raw = msg_data[0][1]
msg = email.message_from_bytes(raw)
subject = msg.get("Subject", "")
if isinstance(subject, bytes):
from email.header import decode_header
parts = decode_header(subject)
subject = "".join(
p.decode(c or "utf-8", errors="replace") if isinstance(p, bytes) else p
for p, c in parts
)
from_addr = msg.get("From", "")
date_str = msg.get("Date", "")
try:
dt = parsedate_to_datetime(date_str)
date_display = dt.strftime("%Y-%m-%d %H:%M")
except Exception:
date_display = date_str[:20] if date_str else ""
preview = ""
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == "text/plain":
try:
preview = part.get_payload(decode=True).decode("utf-8", errors="replace")[:200]
except Exception:
pass
break
else:
try:
preview = msg.get_payload(decode=True).decode("utf-8", errors="replace")[:200]
except Exception:
preview = "(无法解析正文)"
results.append({
"date": date_display,
"from": from_addr[:80],
"subject": subject[:120],
"preview": preview.replace("\n", " ").strip()[:200],
})
if progress and (i + 1) % 500 == 0:
print(f" 已拉取 {i + 1}/{total_fetch} ...", file=sys.stderr)
server.close()
server.logout()
return results
def main():
ap = argparse.ArgumentParser(description="QQ 邮箱 IMAP 拉取")
ap.add_argument("--days", type=int, default=30, help="拉取最近 N 天(与 --all 互斥)")
ap.add_argument("--all", dest="all_mail", action="store_true", help="拉取全部历史邮件")
ap.add_argument("--folder", type=str, default="INBOX", help="指定文件夹,如 INBOX、我的文件夹 等;先 --list-folders 查看")
ap.add_argument("--list-folders", action="store_true", help="列出所有 IMAP 文件夹")
ap.add_argument("--limit", type=int, default=0, help="最多拉取 N 封0 表示不限制")
ap.add_argument("--output", "-o", type=str, default="", help="导出到 JSON 文件")
ap.add_argument("--quiet", "-q", action="store_true", help="不显示进度")
args = ap.parse_args()
if args.list_folders:
for f in list_folders():
print(f)
return
days = 365 * 20 if args.all_mail else args.days
emails = fetch_emails(
days=days,
limit=args.limit,
all_mail=args.all_mail,
progress=not args.quiet,
folder=args.folder,
)
if args.output:
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "w", encoding="utf-8") as f:
json.dump(emails, f, ensure_ascii=False, indent=None)
print(f"已导出 {len(emails)} 封到 {out_path}", file=sys.stderr)
else:
for i, e in enumerate(emails, 1):
print(f"[{i}] {e['date']} | {e['from']}")
print(f" 主题: {e['subject']}")
print(f" 摘要: {e['preview']}")
print("-" * 60)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
QQ 邮箱多文件夹批量拉取 · 收件箱 + 垃圾箱 + 已发送 + 我的文件夹(若已开启)
"""
import json
import sys
from pathlib import Path
from qq_mail_fetch import fetch_emails, AUTH_CODE
OUT_DIR = Path("/Users/karuo/Documents/卡若Ai的文件夹/报告")
FOLDERS = [
("INBOX", "收件箱"),
("Junk", "垃圾箱"),
("Sent Messages", "已发送"),
("Drafts", "草稿箱"),
("Deleted Messages", "已删除"),
]
# 我的文件夹(需在 QQ 设置中勾选「收取我的文件夹」后才能在 IMAP 中看到子目录)
MY_FOLDERS = ["&UXZO1mWHTvZZOQ-"] # 父级,可扩展子目录
def main():
if not AUTH_CODE:
print("请配置 .qq_mail_env 中的授权码")
return 1
results = {}
for folder, label in FOLDERS:
try:
emails = fetch_emails(days=365 * 20, limit=0, all_mail=True, progress=True, folder=folder)
results[label] = {"folder": folder, "count": len(emails), "emails": emails}
out = OUT_DIR / f"qq_{folder.replace(' ', '_').lower()}_export.json"
with open(out, "w", encoding="utf-8") as f:
json.dump(emails, f, ensure_ascii=False, indent=None)
print(f" -> {out.name}: {len(emails)}")
except Exception as e:
print(f"{label} ({folder}): 失败 - {e}")
results[label] = {"folder": folder, "count": 0, "error": str(e)}
for folder in MY_FOLDERS:
try:
emails = fetch_emails(days=365 * 20, limit=0, all_mail=True, progress=True, folder=folder)
results["我的文件夹"] = {"folder": folder, "count": len(emails), "emails": emails}
out = OUT_DIR / "qq_myfolders_export.json"
with open(out, "w", encoding="utf-8") as f:
json.dump(emails, f, ensure_ascii=False, indent=None)
print(f" 我的文件夹 -> {out.name}: {len(emails)}")
except Exception as e:
print(f"我的文件夹: 失败 - {e}")
# 合并统计
total = sum(r.get("count", 0) for r in results.values() if isinstance(r, dict))
print(f"\n合计拉取: {total}")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,65 @@
# 获取 QQ 邮箱其他邮件的办法
## 一、IMAP 已支持的文件夹
| 网页显示 | IMAP 文件夹名 | 状态 | 当前拉取数 |
|:---|:---|:---|:---|
| 收件箱 | INBOX | ✅ 已支持 | 643 |
| 垃圾箱 | Junk | ✅ 已支持 | 22 |
| 已发送 | Sent Messages | ✅ 已支持 | 40 |
| 草稿箱 | Drafts | ✅ 已支持 | 0 |
| 已删除 | Deleted Messages | ✅ 已支持 | 0 |
## 二、获取方式
### 方式 1命令行逐文件夹拉取
```bash
# 收件箱
python3 qq_mail_fetch.py --all -o 报告/qq_inbox_export.json
# 垃圾箱
python3 qq_mail_fetch.py --all --folder Junk -o 报告/qq_junk_export.json
# 已发送(含空格需用引号)
python3 qq_mail_fetch.py --all --folder "Sent Messages" -o 报告/qq_sent_export.json
# 草稿箱
python3 qq_mail_fetch.py --all --folder Drafts -o 报告/qq_drafts_export.json
# 已删除
python3 qq_mail_fetch.py --all --folder "Deleted Messages" -o 报告/qq_deleted_export.json
```
### 方式 2一键批量拉取
```bash
python3 qq_mail_fetch_all.py
```
会依次拉取上述文件夹并导出到 `报告/` 目录。
## 三、我的文件夹14239 封)
**现状**IMAP 中 `&UXZO1mWHTvZZOQ-` 为父级目录,本身无邮件;子文件夹未在 LIST 中列出。
**解决办法**
1. **QQ 邮箱网页** → 右上角 **设置****账户****收取选项**
2. 勾选 **「收取我的文件夹」**
3. 保存后重新执行拉取
4. 执行 `python3 qq_mail_fetch.py --list-folders` 查看是否出现新的子文件夹
## 四、群邮件212 封)
**现状**QQ 邮箱 IMAP 标准文件夹列表中**未发现**群邮件对应目录。
**可能原因**:群邮件可能以标签/虚拟文件夹形式存在,需在网页端开启「收取群邮件」等选项后,才在 IMAP 中可见。
**建议**:在 QQ 邮箱设置中检查「群邮件」「收取选项」相关配置,确认是否支持 IMAP 收取。
## 五、已修复的技术点
1. **含空格文件夹名**`Sent Messages``Deleted Messages` 需在脚本中加双引号,已在本仓库脚本中处理。
2. **只读文件夹**`我的文件夹` 父级为只读,已改为使用 `readonly=True`EXAMINE选择。
3. **批量拉取**:新增 `qq_mail_fetch_all.py` 支持一键拉取多个文件夹。

View File

@@ -1,6 +1,6 @@
{
"access_token": "u-46dpgqFzddzUbmSNaiAQN2l5mWU5k1orhUaaZBM00xP7",
"refresh_token": "ur-4v_0_alr52JanOSvj3DFNul5mgq5k1WpOoaaUNQ00BPj",
"access_token": "u-5jTDb7Rkl57WWjJ3pzwPS6l5moW5k1MXV8aaJxQ00ACm",
"refresh_token": "ur-5ve40_WDh2CUZE2rESveY6l5mOU5k1MphEaaUBM00xCm",
"name": "飞书用户",
"auth_time": "2026-02-23T09:58:30.247057"
}

View File

@@ -43,6 +43,51 @@ ROWS = {
# 场次→按日期列填写时的日期(表头为当月日期 1~31
SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '23'}
# 小程序当日运营数据:日期号 → {访问次数, 访客, 交易金额},填表时自动写入对应日期列
# 数据来源:微信公众平台 → 小程序 → 统计 → 实时访问/概况
# 历史有数据的都填入,批量写入用 write_miniprogram_batch.py
MINIPROGRAM_EXTRA = {
'20': {'访问次数': 45, '访客': 45, '交易金额': 0}, # 2月20日
'21': {'访问次数': 52, '访客': 52, '交易金额': 0}, # 2月21日
'23': {'访问次数': 55, '访客': 55, '交易金额': 0}, # 2月23日
}
def _find_row_for_keyword(vals, keywords):
"""在 vals 中找 A 列包含任一 keyword 的行号1-based"""
for ri, row in enumerate(vals):
a1 = (row[0] if row and len(row) > 0 else '')
a1 = str(a1 or '').strip()
for kw in keywords:
if kw in a1:
return ri + 1
return None
def _write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter):
"""若当日有小程序数据,写入 交易金额、访客、小程序访问 到对应行"""
extra = MINIPROGRAM_EXTRA.get(date_col)
if not extra:
return
# 行→extra 中的键
writes = [
(_find_row_for_keyword(vals, ['交易金额']), extra.get('交易金额', 0)),
(_find_row_for_keyword(vals, ['访客']), extra.get('访客', extra.get('访问次数'))),
(_find_row_for_keyword(vals, ['小程序访问']), extra.get('访问次数')),
]
written = 0
for row_num, val in writes:
if row_num is None or val is None:
continue
rng = f"{sheet_id}!{col_letter}{row_num}"
code, body = update_sheet_range(token, spreadsheet_token, rng, [[_to_cell_value(val)]])
if code == 401 or body.get('code') in (99991677, 99991663):
return
if code == 200 and body.get('code') in (0, None):
written += 1
if written > 0:
print(f'✅ 已写入小程序运营数据2月{date_col}日列):访问次数 {extra.get("访问次数","")}、访客 {extra.get("访客","")}、交易金额 {extra.get("交易金额",0)}')
def load_token():
if not os.path.exists(TOKEN_FILE):
@@ -223,7 +268,7 @@ def main():
values = [_to_cell_value(raw[0])] + [_to_cell_value(raw[i]) for i in range(1, EFFECT_COLS)]
spreadsheet_token = WIKI_NODE_OR_SPREADSHEET_TOKEN
sheet_id = SHEET_ID
range_read = f"{sheet_id}!A1:AG30"
range_read = f"{sheet_id}!A1:AG35"
vals, read_code, read_body = read_sheet_range(token, spreadsheet_token, range_read)
# 401 时刷新 token 并重试读取,确保能定位到日期列
if (read_code == 401 or read_body.get('code') in (99991677, 99991663)) and not vals:
@@ -302,6 +347,7 @@ def main():
ok, msg = _verify_write(spreadsheet_token, sheet_id, col_letter, values, token)
if ok:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter}3:{col_letter}{2+len(values)},共{len(values)}格),校验通过')
_write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter)
_maybe_send_group(session, raw)
return
print(f'⚠️ 写入成功但校验未通过:{msg}')
@@ -324,6 +370,7 @@ def main():
ok, msg = _verify_write(spreadsheet_token, sheet_id, col_letter, values, token)
if ok:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter} 逐格),校验通过')
_write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter)
_maybe_send_group(session, raw)
return
print(f'⚠️ 逐格写入成功但校验未通过:{msg}')

View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
批量写入小程序运营数据到飞书运营报表(小程序访问行)。
- 读取 soul_party_to_feishu_sheet 中的 MINIPROGRAM_EXTRA
- 逐个日期调用 write_miniprogram_to_sheet填入「小程序访问」「访客」「交易金额」
用法python3 write_miniprogram_batch.py
"""
import os
import subprocess
import sys
FEISHU_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, FEISHU_SCRIPT_DIR)
from soul_party_to_feishu_sheet import MINIPROGRAM_EXTRA
def main():
if not MINIPROGRAM_EXTRA:
print('MINIPROGRAM_EXTRA 为空,无可写入数据。')
sys.exit(0)
script = os.path.join(FEISHU_SCRIPT_DIR, 'write_miniprogram_to_sheet.py')
total = 0
for date_col, extra in MINIPROGRAM_EXTRA.items():
access = extra.get('访问次数')
visitor = extra.get('访客', access)
txn = extra.get('交易金额', 0)
if access is None:
continue
cmd = [sys.executable, script, date_col, str(access), str(visitor), str(txn)]
r = subprocess.run(cmd, capture_output=True, text=True, cwd=FEISHU_SCRIPT_DIR)
if r.returncode == 0:
total += 1
print(f'✅ 2月{date_col}日:访问 {access}、访客 {visitor}、交易 {txn}')
else:
print(f'⚠️ 2月{date_col}日 写入失败:{r.stderr or r.stdout}')
print(f'✅ 批量写入完成,共 {total}')
sys.exit(0 if total > 0 else 1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""
小程序运营数据写入飞书运营报表(当日交易金额、访客数、小程序访问次数)。
- 自动查找表格中「交易金额」「访客」「小程序访问」对应行
- 写入指定日期列
用法python3 write_miniprogram_to_sheet.py <日期列号> <访问次数> [访客数] [交易金额]
python3 write_miniprogram_to_sheet.py 23 55 55 0
python3 write_miniprogram_to_sheet.py 23 55 (访客=访问次数,交易金额=0
"""
import os
import sys
import json
import requests
from urllib.parse import quote
FEISHU_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TOKEN_FILE = os.path.join(FEISHU_SCRIPT_DIR, '.feishu_tokens.json')
SPREADSHEET_TOKEN = os.environ.get('FEISHU_SPREADSHEET_TOKEN', 'wikcnIgAGSNHo0t36idHJ668Gfd')
SHEET_ID = os.environ.get('FEISHU_SHEET_ID', '7A3Cy9')
# 指标名 → 匹配关键词A列包含即认为找到
ROW_KEYWORDS = {
'交易金额': ['交易金额'],
'访客': ['访客'],
'小程序访问': ['小程序访问'],
}
def load_token():
if not os.path.exists(TOKEN_FILE):
return None
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
return json.load(f).get('access_token')
def refresh_token():
if not os.path.exists(TOKEN_FILE):
return None
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
r = requests.post(
'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal',
json={'app_id': 'cli_a48818290ef8100d', 'app_secret': 'dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4'},
timeout=10,
)
app_token = (r.json() or {}).get('app_access_token')
if not app_token:
return None
r2 = requests.post(
'https://open.feishu.cn/open-apis/authen/v1/oidc/refresh_access_token',
headers={'Authorization': f'Bearer {app_token}', 'Content-Type': 'application/json'},
json={'grant_type': 'refresh_token', 'refresh_token': data.get('refresh_token')},
timeout=10,
)
out = r2.json()
if out.get('code') == 0 and out.get('data', {}).get('access_token'):
data['access_token'] = out['data']['access_token']
data['refresh_token'] = out['data'].get('refresh_token', data.get('refresh_token'))
with open(TOKEN_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return data['access_token']
return None
def read_range(token, range_str):
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{quote(range_str, safe="")}'
r = requests.get(url, headers={'Authorization': f'Bearer {token}'}, timeout=15)
if r.status_code != 200:
return None
body = r.json()
if body.get('code') != 0:
return None
return (body.get('data') or {}).get('valueRange', {}).get('values') or []
def update_cell(token, range_str, value, value_input_option='USER_ENTERED'):
if range_str.count('!') == 1 and ':' not in range_str.split('!')[1]:
range_str = range_str + ':' + range_str.split('!')[1]
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values'
params = {'valueInputOption': value_input_option}
v = value if value is not None and value != '' else ''
payload = {'valueRange': {'range': range_str, 'values': [[v]]}}
r = requests.put(
url, params=params,
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},
json=payload, timeout=15,
)
try:
return r.status_code, r.json()
except Exception:
return r.status_code, {}
def _col_letter(n):
s = ''
while True:
s = chr(65 + n % 26) + s
n = n // 26
if n <= 0:
break
return s
def find_row_for_keyword(vals, keyword_list):
"""在 vals 中找 A 列包含任一 keyword 的行号1-based"""
for ri, row in enumerate(vals):
a1 = (row[0] if row and len(row) > 0 else '')
a1 = str(a1 or '').strip()
for kw in keyword_list:
if kw in a1:
return ri + 1
return None
def main():
if len(sys.argv) < 3:
print('用法python3 write_miniprogram_to_sheet.py <日期列号> <访问次数> [访客数] [交易金额]')
print('python3 write_miniprogram_to_sheet.py 23 55 55 0')
sys.exit(1)
date_col_str = sys.argv[1].strip()
access_count = sys.argv[2].strip()
visitor_count = sys.argv[3].strip() if len(sys.argv) > 3 else access_count
transaction = sys.argv[4].strip() if len(sys.argv) > 4 else '0'
token = load_token() or refresh_token()
if not token:
print('❌ 无法获取飞书 Token')
sys.exit(1)
vals = read_range(token, f'{SHEET_ID}!A1:AG35')
if not vals or len(vals) < 2:
print('❌ 读取表格失败')
sys.exit(1)
header = vals[0]
col_idx = None
for idx, cell in enumerate(header):
if str(cell).strip() == date_col_str:
col_idx = idx
break
if col_idx is None:
print(f'❌ 未找到日期列 {date_col_str}')
sys.exit(1)
# 查找三行:交易金额、访客、小程序访问
row_txn = find_row_for_keyword(vals, ROW_KEYWORDS['交易金额'])
row_visitor = find_row_for_keyword(vals, ROW_KEYWORDS['访客'])
row_access = find_row_for_keyword(vals, ROW_KEYWORDS['小程序访问'])
# 若未找到「访客」单独行,可能和「小程序访问」共用,用访问次数
if not row_visitor and row_access:
row_visitor = row_access # 同一行填访客与访问
col_letter = _col_letter(col_idx)
written = 0
def _write_one(row_num, val, name):
nonlocal written, token
if row_num is None:
return
rng = f'{SHEET_ID}!{col_letter}{row_num}'
code, body = update_cell(token, rng, val)
if code == 401 or body.get('code') in (99991677, 99991663):
t = refresh_token()
if t:
token = t
code, body = update_cell(token, rng, val)
if code == 200 and body.get('code') in (0, None):
print(f'✅ 已写入 {name} → 2月{date_col_str}日列:{val}')
written += 1
else:
print(f'⚠️ 写入 {name} 失败:{code} {body}')
_write_one(row_txn, transaction, '交易金额')
_write_one(row_visitor, visitor_count, '访客')
_write_one(row_access, access_count, '小程序访问')
if written == 0:
print('❌ 未找到可写入的行,请确认表格 A 列有「交易金额」「访客」「小程序访问」等指标')
sys.exit(1)
print(f'✅ 小程序运营数据已填入 2月{date_col_str}日列,共 {written}')
if __name__ == '__main__':
main()

View File

@@ -53,7 +53,7 @@ python3 write_party_minutes_from_txt.py "/path/to/soul 派对 106场 20260221.tx
| 表格链接 | https://cunkebao.feishu.cn/wiki/wikcnIgAGSNHo0t36idHJ668Gfd?sheet=7A3Cy9 |
| spreadsheet_token | `wikcnIgAGSNHo0t36idHJ668Gfd` |
| sheet_id | `7A3Cy9` |
| 表格结构 | A 列=指标名,第 1 行=日期1、2…21…第 3~12 行=效果数据,第 28 行=今日总结 |
| 表格结构 | A 列=指标名,第 1 行=日期1、2…21…第 3~12 行=效果数据,第 15 行=小程序访问,第 28 行=今日总结 |
### 1.3 飞书群 Webhook
@@ -89,6 +89,29 @@ python3 write_party_minutes_from_txt.py "/path/to/soul 派对 106场 20260221.tx
|:---|:---|:---|
| `feishu_write_minutes_to_sheet.py` | 会议纪要/派对总结**图片**上传到对应单元格 | `python3 feishu_write_minutes_to_sheet.py [内部图] [派对图]` |
| `feishu_sheet_monthly_stats.py` | 月度运营数据统计 | `python3 feishu_sheet_monthly_stats.py 2``all` |
| `write_miniprogram_to_sheet.py` | **单独**写入小程序三核心数据(访问次数、访客、交易金额) | `python3 write_miniprogram_to_sheet.py 23 55 55 0` |
### 2.3 小程序运营数据(自动写入)
每日填表时,若在 `soul_party_to_feishu_sheet.py` 中配置了 `MINIPROGRAM_EXTRA`,会**自动**把当日小程序三核心数据写入对应日期列:
| 指标 | 数据来源 | 行A 列关键词) |
|:---|:---|:---|
| 访问次数 | 微信公众平台 → 小程序 → 统计 → 实时访问 | 小程序访问 |
| 访客 | 同上 | 访客 |
| 交易金额 | 同上 | 交易金额 |
**配置方式**(在 `soul_party_to_feishu_sheet.py` 中):
```python
MINIPROGRAM_EXTRA = {
'23': {'访问次数': 55, '访客': 55, '交易金额': 0}, # 2月23日
}
```
- 数据来源:微信公众平台 → 小程序 → 统计,每日手动查看后填入
- 仅填表当天:运行 `python3 soul_party_to_feishu_sheet.py 107` 时,若 107 场对应 2月23日`MINIPROGRAM_EXTRA``'23'`,则自动写入
- 单独写入:`python3 write_miniprogram_to_sheet.py 23 55 55 0`(日期列号 访问次数 访客 交易金额)
---
@@ -146,6 +169,7 @@ python3 write_party_minutes_from_txt.py "/Users/karuo/Downloads/soul 派对 107
成功输出示例:
```
✅ 已写入飞书表格107场 效果数据(竖列 W3:W12共10格校验通过
✅ 已写入小程序运营数据2月23日列访问次数 55、访客 55、交易金额 0
✅ 已同步推送到飞书群(竖状格式)
✅ 已写入派对智能纪要到「今日总结」→ 2月22日列校验通过
```
@@ -282,6 +306,8 @@ export FEISHU_APP_SECRET=dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4
第11行 增加关注 | | | | | xx | xx | |
第12行 最高在线 | | | | | xx | xx | |
...
第15行 小程序访问| | | | | xx | xx | | ← 访问次数、访客、交易金额写对应行
...
第28行 今日总结 | | | | | xx | xx | | ← 智能纪要写这里
```
@@ -289,7 +315,7 @@ export FEISHU_APP_SECRET=dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4
## 十、新增场次模板
每次新增场次,只需改 `soul_party_to_feishu_sheet.py` 两处:
每次新增场次,只需改 `soul_party_to_feishu_sheet.py`到三处:
```python
# 1. ROWS 字典加一行
@@ -299,6 +325,9 @@ export FEISHU_APP_SECRET=dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4
SESSION_DATE_COLUMN = {..., 'NEW': '日期号'}
# 3. _maybe_send_group 内 date_label 和 src_date 可选加映射(可选,不加则不发群)
# 4. 若当日有小程序数据,在 MINIPROGRAM_EXTRA 中加:
# MINIPROGRAM_EXTRA = {..., '23': {'访问次数': 55, '访客': 55, '交易金额': 0}}
```
---

View File

@@ -0,0 +1,43 @@
---
name: 公司财务
description: 芸归喜、卡卡猫等公司主体的收支、月度报表、工资表。仅公司开支,不含家庭。触发词:公司财务、芸归喜、卡卡猫、公司报表、公司开支。
group: 土
triggers: 公司财务、芸归喜、卡卡猫、公司报表、公司开支、中信0405、网商9532
owner: 土簿
version: "1.0"
updated: "2026-02-08"
parent: 财务管理
---
# 公司财务
卡若名下公司主体的财务汇总、月度报表、工资表。**仅含公司收支,不含家庭财务**。
## 关联路径
| 项目 | 路径 |
|:---|:---|
| 月度报表 | `4、财务/报告/`(如 2026年1月财务报表_完整表格.md、2026年2月财务报表_完整表格.md |
| 工资表 | `4、财务/报告/`(如 2026年2月工资表.md |
| 重点机构交易明细 | `4、财务/财务报表/重点机构与主体_交易明细.csv` |
| 数据解析脚本 | `4、财务/scripts/parse_feb_2026_data.py` |
## 核心能力
1. **报表**:生成/更新公司月度财务报表(芸归喜、卡卡猫、云消费、飞书工资)
2. **数据来源**:中信 0405**卡卡猫**)、网商 9532**芸归喜**)、腾讯云、飞书钱袋子
3. **执行**:运行 `parse_feb_2026_data.py [YYYY-MM]` 解析指定月份后更新报表
## 报表范围(仅公司)
- 云消费(腾讯云)
- 卡卡猫(中信 0405收支
- 芸归喜(网商 9532收支
- 飞书·工资/报销
- **不含**:家庭、鲨鱼记账
## 生成月度报表步骤
1. 执行 `python3 4、财务/scripts/parse_feb_2026_data.py YYYY-MM` 获取数据
2. 更新 `4、财务/报告/YYYY年MM月财务报表_完整表格.md`
3. 工资表从飞书钱袋子「2026年卡若公司工资表」同步

View File

@@ -0,0 +1,39 @@
---
name: 家庭财务
description: 家庭收支、鲨鱼记账、个人/家庭账单。与公司财务分离。触发词:家庭财务、鲨鱼、家庭收支、家庭报表。
group: 土
triggers: 家庭财务、鲨鱼记账、家庭收支、家庭报表
owner: 土簿
version: "1.0"
updated: "2026-02-08"
parent: 财务管理
---
# 家庭财务
卡若家庭收支、鲨鱼记账、个人账单。**与公司财务分离,仅家庭维度**。
## 关联路径
| 项目 | 路径 |
|:---|:---|
| 鲨鱼记账明细 | `4、财务/家庭财务/` 或 鲨鱼导出 |
| 家庭报表 | 需从鲨鱼记账按月导出后填入 |
## 核心能力
1. **家庭收支**:鲨鱼记账月度汇总
2. **数据来源**:鲨鱼记账导出(收入、支出、缺口/结余)
3. **与公司分离**:公司报表不含家庭;家庭报表单独维护
## 报表范围(仅家庭)
- 收入
- 支出
- 缺口/结余
## 生成家庭月度报表步骤
1. 从鲨鱼记账导出指定月份(如 2026-02
2. 汇总收入、支出、结余
3. 写入家庭财务专用报表或 `4、财务/报告/` 下家庭分表

View File

@@ -1,7 +1,7 @@
# 卡若AI 技能注册表Skill Registry
> **一张表查所有技能**。任何 AI 拿到这张表,就能按关键词找到对应技能的 SKILL.md 路径并执行。
> 57 技能 | 14 成员 | 5 负责人
> 58 技能 | 14 成员 | 5 负责人
> 版本5.0 | 更新2026-02-16
---
@@ -56,6 +56,7 @@
| W09 | 小程序管理 | 水桥 | 小程序、微信小程序 | `02_卡人/水桥_平台对接/小程序管理/SKILL.md` | 微信小程序发布与维护 |
| W10 | Soul文章上传 | 水桥 | **Soul文章上传、Soul派对文章、第9章上传、soul上传** | `02_卡人/水桥_平台对接/Soul文章上传/SKILL.md` | 《一场soul的创业实验》第9章文章写好后上传到小程序id 已存在则更新不重复 |
| W11 | Soul派对运营报表 | 水桥 | **运营报表、派对填表、派对截图填表发群、派对纪要、智能纪要、106场、107场、本月运营数据** | `02_卡人/水桥_平台对接/飞书管理/运营报表_SKILL.md` | 派对截图+TXT→飞书运营报表→智能纪要→飞书群推送含Token自刷新与写入校验 |
| W12 | MCP 搜索与连接 | 水桥 | **MCP、找MCP、连接MCP、MCP搜索、发现MCP、添加MCP、需要MCP、MCP安装、MCP发现、查MCP、装MCP** | `02_卡人/水桥_平台对接/MCP管理/SKILL.md` | 搜索 5000+ MCP 服务器→生成安装配置→写入 Cursor/Claude 等 |
## 木组 · 卡木(产品内容创造)
@@ -93,6 +94,8 @@
| E03 | 流量自动化 | 土渠 | 刷流量、SEO | `05_卡土/土渠_流量招商/流量自动化/SKILL.md` | SEO、流量投放自动化 |
| E04 | 手机流量自动操作 | 土渠 | 手机自动化、AutoGLM | `05_卡土/土渠_流量招商/手机与网页流量自动操作/SKILL.md` | 手机 App 自动化操作 |
| E05 | 财务管理 | 土簿 | 财务、报表、银行 | `05_卡土/土簿_财务管理/财务管理/SKILL.md` | 收支记录、财务报表 |
| E05a | 公司财务 | 土簿 | **公司财务、芸归喜、卡卡猫、公司报表、公司开支** | `05_卡土/土簿_财务管理/公司财务/SKILL.md` | 仅公司收支、月度报表、工资表 |
| E05b | 家庭财务 | 土簿 | **家庭财务、鲨鱼记账、家庭收支** | `05_卡土/土簿_财务管理/家庭财务/SKILL.md` | 家庭收支、鲨鱼记账,与公司分离 |
| E06 | 商业工具集(财务) | 土簿 | 商业分析 | `05_卡土/土簿_财务管理/商业工具集/SKILL.md` | 财务视角的商业分析 |
---
@@ -117,8 +120,8 @@
| 组 | 负责人 | 成员数 | 技能数 |
|:--|:---|:--|:--|
| 金 | 卡资 | 2 | 20 |
| 水 | 卡人 | 3 | 11 |
| 水 | 卡人 | 3 | 12 |
| 木 | 卡木 | 3 | 6 |
| 火 | 卡火 | 4 | 13 |
| 土 | 卡土 | 4 | 7 |
| **合计** | **5** | **14** | **57** |
| **合计** | **5** | **14** | **58** |

View File

@@ -117,3 +117,4 @@
| 2026-02-23 20:58:31 | 🔄 卡若AI 同步 2026-02-23 20:58 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 |
| 2026-02-23 21:08:00 | 🔄 卡若AI 同步 2026-02-23 21:07 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 |
| 2026-02-23 21:19:17 | 🔄 卡若AI 同步 2026-02-23 21:19 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 |
| 2026-02-23 21:31:11 | 🔄 卡若AI 同步 2026-02-23 21:31 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 |

View File

@@ -120,3 +120,4 @@
| 2026-02-23 20:58:31 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-23 20:58 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-23 21:08:00 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-23 21:07 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-23 21:19:17 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-23 21:19 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-23 21:31:11 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-23 21:31 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |

View File

@@ -0,0 +1,112 @@
# 家里 NASDS213j整体分析报表
> **生成时间**2026-02
> **数据来源**:端口扫描、现有文档、分布式算力管控 Skill、双 NAS 区分文档
> **说明**SSH/DSM API 当前未连接,部分为历史记录与文档汇总
---
## 一、设备概览
| 项目 | 值 |
|:-----|:---|
| **型号** | Synology DS213j (synology_armada370_213j) |
| **主机名** | DiskStation / DiskStation.local |
| **内网 IP** | 192.168.110.29 |
| **外网域名** | opennas2.quwanzhi.comfrpc 穿透) |
| **MAC 地址** | 00:11:32:30:4c:4fSynology OUI |
| **用途** | 家庭存储、Time Machine 备份、PCDN网心云 |
---
## 二、硬件与系统
| 项目 | 规格 |
|:-----|:-----|
| **CPU** | Marvell Armada370ARMv7 |
| **架构** | armv7l32 位 ARM |
| **内存** | 497MB |
| **内核** | Linux 3.2.40 |
| **文件系统** | ext4RAIDmd2 |
| **Docker** | ❌ 不支持(内核 3.x 无 cgroup |
---
## 三、存储空间
| 指标 | 值 | 备注 |
|:-----|:---|:-----|
| **总容量** | 5.4TB | 双盘 RAID |
| **已用** | 约 3.4TB4.6TB | 不同时间点记录 |
| **可用** | 约 885GB2.0TB | 视时段与共享不同 |
| **Time Machine 共享** | 2.18TB 可用 | 系统设置中「共享」卷 |
| **空间占比** | 约 64%84% | 建议保持 100GB+ 余量 |
---
## 四、网络与端口
### 4.1 当前开放端口(本次扫描)
| 端口 | 服务 | 状态 |
|:-----|:-----|:-----|
| 22 | SSH | 开放 |
| 80 | HTTP | 开放 |
| 139 | NetBIOS | 开放 |
| 443 | HTTPS | 开放 |
| 445 | SMB | 开放 |
| 5000 | DSM HTTP | 开放 |
| 5001 | DSM HTTPS | 开放 |
### 4.2 内网穿透frpc → opennas2.quwanzhi.com
| 服务 | 外网端口 | 外网访问 |
|:-----|:---------|:---------|
| SSH | 22202 | `ssh admin@opennas2.quwanzhi.com -p 22202` |
| DSM | 5002 / 80 | http://opennas2.quwanzhi.com:5002 或 :80 |
| SMB | 4452 | smb://opennas2.quwanzhi.com:4452/共享(需在 frpc 中配置) |
| FTP / rsync / MariaDB 等 | 见双 NAS 文档 | — |
---
## 五、已部署服务
| 服务 | 说明 | 状态 |
|:-----|:-----|:-----|
| **DSM** | 群晖管理界面 | ✅ 运行中 |
| **SMB/AFP** | 文件共享、Time Machine | ✅ 运行中 |
| **frpc** | 内网穿透opennas2 | ✅ 配置在 crontab 检活 |
| **网心云 wxedge** | PCDNchroot 部署) | ✅ 运行中SN: CTWX09Y9Q2ILI4PV |
---
## 六、资源与负载(历史记录)
| 项目 | 状态 |
|:-----|:-----|
| **CPU 负载** | 曾出现 udevd 异常,已处理,当前约 1.6 |
| **内存** | 497MB 总量,约 67MB170MB 可用(视 PCDN 运行情况) |
| **磁盘** | 已清理 core dump空间从 100% 恢复到约 84% |
---
## 七、访问方式
| 场景 | 地址 |
|:-----|:-----|
| **内网 DSM** | http://192.168.110.29:5000 |
| **外网 DSM** | http://opennas2.quwanzhi.com:5002 |
| **Time Machine** | 共享 - DiskStation.local或 smb://192.168.110.29/共享) |
| **外网 1TB 挂载** | smb://opennas2.quwanzhi.com:4452/共享(需先添加 frpc SMB |
---
## 八、风险与建议
| 类型 | 说明 |
|:-----|:-----|
| **硬件老化** | DS213j 为较旧型号ARM32 + 497MB不适合再增重型服务 |
| **无 Docker** | 无法运行容器,扩展能力有限 |
| **外网 SSH 不稳定** | 外网 22202 有时不可达与家庭网络、frpc 有关 |
| **空间** | 定期清理,保持 10% 以上可用空间 |
| **建议** | 维持当前用途(存储 + Time Machine + PCDN避免新增高负载服务 |