🔄 卡若AI 同步 2026-02-24 19:59 | 更新:总索引与入口、水溪整理归档、卡木、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 12 个
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -24,6 +24,10 @@ sync_tokens.env
|
||||
**/智能纪要/脚本/feishu_user_token.txt
|
||||
**/智能纪要/脚本/cookie_minutes.txt
|
||||
|
||||
# 卡若AI 网关:多租户配置与访问日志(不入库)
|
||||
运营中枢/scripts/karuo_ai_gateway/config/gateway.yaml
|
||||
运营中枢/工作台/karuo_ai_gateway_access.jsonl
|
||||
|
||||
# Node / 前端
|
||||
node_modules/
|
||||
.next/
|
||||
@@ -90,6 +94,7 @@ _大文件外置/财务管理_data/chat.snapshot_收集.db
|
||||
# === 自动排除:超过20MB的文件(脚本自动管理,勿手动修改)===
|
||||
.venv/lib/python3.14/site-packages/playwright/driver/node
|
||||
.venv_mem0/lib/python3.14/site-packages/grpc/_cython/cygrpc.cpython-314-darwin.so
|
||||
.venv_ppt/lib/python3.14/site-packages/playwright/driver/node
|
||||
01_卡资(金)/金仓_存储备份/大文件外置/消息中枢_dist/windows控制包.zip
|
||||
01_卡资(金)/金仓_存储备份/大文件外置/视频切片_models/ggml-small.bin
|
||||
01_卡资(金)/金仓_存储备份/大文件外置/财务管理_data/chat.snapshot_data.db
|
||||
@@ -98,4 +103,5 @@ _大文件外置/财务管理_data/chat.snapshot_收集.db
|
||||
03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/.cache/webpack/remotion-production-4.0.427/a233e9cccba253c3b0157f54cad843b8/0.pack
|
||||
03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/.remotion/chrome-headless-shell/mac-x64/chrome-headless-shell-mac-x64/chrome-headless-shell
|
||||
03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/@rspack/binding-darwin-x64/rspack.darwin-x64.node
|
||||
03_卡木(木)/木果_项目模板/PPT制作/.venv/lib/python3.14/site-packages/playwright/driver/node
|
||||
# === 自动排除结束 ===
|
||||
|
||||
@@ -1 +1 @@
|
||||
2026-02-22
|
||||
2026-02-24
|
||||
622
03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html
Normal file
622
03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html
Normal file
@@ -0,0 +1,622 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, height=720">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
.slide {
|
||||
width: 1280px; height: 720px;
|
||||
display: flex; flex-direction: column;
|
||||
padding: 64px 80px;
|
||||
background: linear-gradient(160deg, #F5F5FA 0%, #EBEBF0 35%, #F0F0F5 100%);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.slide::before {
|
||||
content: ''; position: absolute; top: -40%; right: -25%;
|
||||
width: 70%; height: 90%;
|
||||
background: radial-gradient(ellipse, rgba(0,122,255,0.10) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.slide::after {
|
||||
content: ''; position: absolute; bottom: -45%; left: -22%;
|
||||
width: 70%; height: 95%;
|
||||
background: radial-gradient(ellipse, rgba(52,199,89,0.10) 0%, transparent 58%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.glass {
|
||||
background: rgba(255,255,255,0.60);
|
||||
backdrop-filter: blur(48px); -webkit-backdrop-filter: blur(48px);
|
||||
border: 1px solid rgba(255,255,255,0.72);
|
||||
border-radius: 28px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.04), 0 0 0 1px rgba(255,255,255,0.8) inset;
|
||||
}
|
||||
.glass-strong {
|
||||
background: rgba(255,255,255,0.74);
|
||||
backdrop-filter: blur(56px); -webkit-backdrop-filter: blur(56px);
|
||||
border: 1px solid rgba(255,255,255,0.78);
|
||||
border-radius: 32px;
|
||||
box-shadow: 0 12px 40px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.9) inset;
|
||||
}
|
||||
.text-dark { color: #1D1D1F; }
|
||||
.text-muted { color: #8E8E93; }
|
||||
h1 { font-size: 52px; font-weight: 800; letter-spacing: -0.03em; }
|
||||
h2 { font-size: 18px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; color: #007AFF; }
|
||||
.kicker { font-size: 14px; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(0,0,0,0.45); }
|
||||
.row { display: flex; gap: 36px; }
|
||||
.col { display: flex; flex-direction: column; gap: 18px; }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 10px;
|
||||
padding: 10px 14px; border-radius: 999px;
|
||||
background: rgba(0,122,255,0.10);
|
||||
color: #0B5ED7; font-weight: 700; font-size: 14px;
|
||||
border: 1px solid rgba(0,122,255,0.14);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tag {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
font-size: 14px; font-weight: 700;
|
||||
color: rgba(0,0,0,0.58);
|
||||
padding: 8px 12px; border-radius: 12px;
|
||||
background: rgba(255,255,255,0.55);
|
||||
border: 1px solid rgba(255,255,255,0.7);
|
||||
}
|
||||
.icon { font-size: 42px; line-height: 1; }
|
||||
.small { font-size: 16px; line-height: 1.7; color: rgba(0,0,0,0.68); }
|
||||
.list p { font-size: 20px; line-height: 1.85; color: #1D1D1F; }
|
||||
.list p span { color: #8E8E93; }
|
||||
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.metric { padding: 18px 20px; border-radius: 18px; background: rgba(255,255,255,0.62); border: 1px solid rgba(255,255,255,0.75); }
|
||||
.metric .v { font-size: 28px; font-weight: 900; color: #1D1D1F; letter-spacing: -0.02em; }
|
||||
.metric .k { font-size: 13px; font-weight: 800; color: rgba(0,0,0,0.50); letter-spacing: 0.14em; text-transform: uppercase; margin-top: 6px; }
|
||||
.img-card { width: 420px; height: 420px; border-radius: 22px; overflow: hidden; padding: 16px; }
|
||||
.img-card.wide { width: 520px; height: 360px; }
|
||||
.note { padding: 14px 16px; border-radius: 16px; background: rgba(255,149,0,0.12); border: 1px solid rgba(255,149,0,0.18); color: rgba(0,0,0,0.72); font-weight: 600; }
|
||||
.ok { background: rgba(52,199,89,0.12); border-color: rgba(52,199,89,0.18); }
|
||||
.bad { background: rgba(255,59,48,0.10); border-color: rgba(255,59,48,0.16); }
|
||||
.mono { font-variant-numeric: tabular-nums; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Slide 1: 封面 -->
|
||||
<div class="slide" id="slide-1" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 56px 72px; text-align:center; width: 980px;">
|
||||
<div class="kicker" style="margin-bottom: 10px;">CFO Plain Talk · Multi-Dimensional</div>
|
||||
<h1 class="text-dark" style="margin-bottom: 18px;">公司财务盘点与现金流体检</h1>
|
||||
<p class="text-muted" style="font-size: 22px; font-weight: 700;">大白话版本:把钱从哪来、花到哪去、哪里不对劲,一次说清楚</p>
|
||||
<div style="display:flex; justify-content:center; gap:12px; margin-top: 26px; flex-wrap: wrap;">
|
||||
<span class="pill">🧾 统一口径</span>
|
||||
<span class="pill">💳 银行-卡号</span>
|
||||
<span class="pill">🧹 同卡号去重</span>
|
||||
<span class="pill">📈 多维报表</span>
|
||||
<span class="pill">🧠 CFO 结论</span>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 18px;">数据口径:统一流水去重后(支付宝 + 短信银行 + 公司收支:卡卡猫→中信0405)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: 一句话结论 -->
|
||||
<div class="slide" id="slide-2" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 28px;">Executive Summary</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 34px 40px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🧠</span>
|
||||
<span class="tag">一句话</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>这份口径里:<span>净现金流是 -214 万</span>。</p>
|
||||
<p>但最大问题不是“真亏这么多”,而是:<span>短信里混进了额度/提醒,被当成支出</span>。</p>
|
||||
<p>我的结论:<span>先把口径变干净,再谈控制成本和增长</span>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
✅ 你真正要盯的:中信银行-0405(卡卡猫)回款节奏 + 固定支出节奏 + 现金余粮。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:抽象“漏斗 + 数据清洗” -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g2" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#0A84FF" stop-opacity="0.18"/>
|
||||
<stop offset="55%" stop-color="#34C759" stop-opacity="0.14"/>
|
||||
<stop offset="100%" stop-color="#FF9500" stop-opacity="0.12"/>
|
||||
</linearGradient>
|
||||
<filter id="s2" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="14" stdDeviation="12" flood-color="#000" flood-opacity="0.12"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="url(#g2)"/>
|
||||
<g filter="url(#s2)">
|
||||
<rect x="70" y="70" width="280" height="140" rx="18" fill="rgba(255,255,255,0.75)" stroke="rgba(255,255,255,0.9)"/>
|
||||
<text x="95" y="115" font-size="16" font-weight="800" fill="rgba(0,0,0,0.72)">输入:三路数据</text>
|
||||
<text x="95" y="145" font-size="14" font-weight="700" fill="rgba(0,0,0,0.55)">支付宝 / 短信 / 公司收支</text>
|
||||
<rect x="110" y="235" width="200" height="34" rx="12" fill="rgba(10,132,255,0.14)" stroke="rgba(10,132,255,0.18)"/>
|
||||
<text x="125" y="258" font-size="14" font-weight="800" fill="rgba(0,0,0,0.70)">清洗 + 去重 + 合并</text>
|
||||
<path d="M210 210 L170 235 L250 235 Z" fill="rgba(255,255,255,0.85)"/>
|
||||
<rect x="92" y="300" width="236" height="78" rx="18" fill="rgba(255,255,255,0.78)" stroke="rgba(255,255,255,0.92)"/>
|
||||
<text x="115" y="338" font-size="18" font-weight="900" fill="rgba(0,0,0,0.80)">输出:统一报表</text>
|
||||
<text x="115" y="363" font-size="14" font-weight="700" fill="rgba(0,0,0,0.55)">年度 / 月度 / 账户 / Top 对方</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 3: 口径与规则 -->
|
||||
<div class="slide" id="slide-3" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 28px;">Data Rules</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 32px 36px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 12px;">
|
||||
<span class="icon">🧾</span>
|
||||
<span class="tag">口径说人话</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>① 银行:<span>按“银行-卡号”分开</span>(比如 中信银行-0405)。</p>
|
||||
<p>② 合并:<span>卡卡猫 = 中信银行-0405</span>(短信和公司收支算同一户)。</p>
|
||||
<p>③ 去重:<span>同日期 + 同金额 + 同方向 + 同对方</span> → 直接去掉重复。</p>
|
||||
<p>④ 时间:<span>每一笔都强制带日期+时间节点</span>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass" style="padding: 18px 22px;">
|
||||
<div class="small mono">你现在的“统一流水去重后”总笔数:6,446 笔</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:流程图 -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#FFFFFF" stop-opacity="0.88"/>
|
||||
<stop offset="100%" stop-color="#FFFFFF" stop-opacity="0.58"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="url(#g3)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<rect x="36" y="46" width="148" height="64" rx="16" fill="rgba(10,132,255,0.12)" stroke="rgba(10,132,255,0.18)"/>
|
||||
<text x="52" y="75" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">短信银行</text>
|
||||
<text x="52" y="98" font-size="12" font-weight="700" fill="rgba(0,0,0,0.52)">银行-卡号</text>
|
||||
|
||||
<rect x="36" y="134" width="148" height="64" rx="16" fill="rgba(52,199,89,0.12)" stroke="rgba(52,199,89,0.18)"/>
|
||||
<text x="52" y="163" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">公司收支</text>
|
||||
<text x="52" y="186" font-size="12" font-weight="700" fill="rgba(0,0,0,0.52)">卡卡猫→0405</text>
|
||||
|
||||
<rect x="36" y="222" width="148" height="64" rx="16" fill="rgba(255,149,0,0.12)" stroke="rgba(255,149,0,0.18)"/>
|
||||
<text x="52" y="251" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">支付宝</text>
|
||||
<text x="52" y="274" font-size="12" font-weight="700" fill="rgba(0,0,0,0.52)">明细交易</text>
|
||||
|
||||
<path d="M210 78 C250 78 250 78 290 78" stroke="rgba(0,0,0,0.22)" stroke-width="3" fill="none"/>
|
||||
<path d="M210 166 C250 166 250 166 290 166" stroke="rgba(0,0,0,0.22)" stroke-width="3" fill="none"/>
|
||||
<path d="M210 254 C250 254 250 254 290 254" stroke="rgba(0,0,0,0.22)" stroke-width="3" fill="none"/>
|
||||
|
||||
<rect x="294" y="88" width="190" height="112" rx="18" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.92)"/>
|
||||
<text x="314" y="122" font-size="16" font-weight="900" fill="rgba(0,0,0,0.74)">清洗 + 合并</text>
|
||||
<text x="314" y="148" font-size="12" font-weight="700" fill="rgba(0,0,0,0.50)">按卡号 / 抽对方名</text>
|
||||
<text x="314" y="170" font-size="12" font-weight="700" fill="rgba(0,0,0,0.50)">同卡号去重</text>
|
||||
|
||||
<rect x="294" y="216" width="190" height="96" rx="18" fill="rgba(255,255,255,0.82)" stroke="rgba(255,255,255,0.92)"/>
|
||||
<text x="314" y="250" font-size="16" font-weight="900" fill="rgba(0,0,0,0.74)">多维报表</text>
|
||||
<text x="314" y="276" font-size="12" font-weight="700" fill="rgba(0,0,0,0.50)">年/季/月/账户/Top</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 4: 总览数字 -->
|
||||
<div class="slide" id="slide-4" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Numbers</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">📈</span>
|
||||
<span class="tag">先看四个数</span>
|
||||
</div>
|
||||
<div class="grid2">
|
||||
<div class="metric">
|
||||
<div class="v mono">2,198,237.84</div>
|
||||
<div class="k">总收入(元)</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="v mono">4,342,873.62</div>
|
||||
<div class="k">总支出(元)</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="v mono" style="color:#FF3B30;">-2,144,635.78</div>
|
||||
<div class="k">净现金流(元)</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="v mono">50.6%</div>
|
||||
<div class="k">收支比</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 10px;">大白话:<b>进来的钱 ≈ 220 万</b>,出去的钱 ≈ <b>434 万</b>,所以净流出 ≈ <b>214 万</b>。</div>
|
||||
</div>
|
||||
<div class="note bad" style="margin-top: 14px;">
|
||||
⚠️ 重要提醒:支出里有大量“短信噪音”(额度/提醒/营销),这会把支出夸大。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:环形占比 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="s4" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="16" stdDeviation="14" flood-color="#000" flood-opacity="0.10"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.38)"/>
|
||||
<g filter="url(#s4)">
|
||||
<circle cx="210" cy="210" r="130" fill="none" stroke="rgba(0,0,0,0.08)" stroke-width="26"/>
|
||||
<!-- 收入弧(约 33.6%) -->
|
||||
<circle cx="210" cy="210" r="130" fill="none" stroke="rgba(10,132,255,0.65)" stroke-width="26"
|
||||
stroke-dasharray="273 545" stroke-linecap="round" transform="rotate(-90 210 210)"/>
|
||||
<!-- 支出弧(剩余) -->
|
||||
<circle cx="210" cy="210" r="130" fill="none" stroke="rgba(255,59,48,0.55)" stroke-width="26"
|
||||
stroke-dasharray="545 545" stroke-linecap="round" transform="rotate(31 210 210)"/>
|
||||
<text x="210" y="204" text-anchor="middle" font-size="22" font-weight="900" fill="rgba(0,0,0,0.78)">收入 vs 支出</text>
|
||||
<text x="210" y="236" text-anchor="middle" font-size="14" font-weight="800" fill="rgba(0,0,0,0.54)">收支比 50.6%</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="74" y="320" width="272" height="56" rx="16" fill="rgba(255,255,255,0.72)" stroke="rgba(255,255,255,0.9)"/>
|
||||
<text x="100" y="352" font-size="14" font-weight="800" fill="rgba(0,0,0,0.70)">蓝:收入 220 万</text>
|
||||
<text x="240" y="352" font-size="14" font-weight="800" fill="rgba(0,0,0,0.70)">红:支出 434 万</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 5: 年度趋势 -->
|
||||
<div class="slide" id="slide-5" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Trend</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">📉</span>
|
||||
<span class="tag">年度怎么走</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>2017~2024:<span>整体平稳(收支接近)</span>。</p>
|
||||
<p>2025:<span>波动大(收入 110.8 万,支出 145.6 万)</span>。</p>
|
||||
<p>2026:<span>支出异常大(184.9 万)</span> → <b>优先核对短信噪音</b>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
CFO 大白话:趋势里只要出现“突然暴涨/暴跌”,第一件事不是惊慌,是先问:<b>数据口径是不是变了</b>?
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:柱状趋势(简化) -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">净额(越低越危险)</text>
|
||||
<!-- baseline -->
|
||||
<line x1="28" y1="290" x2="492" y2="290" stroke="rgba(0,0,0,0.10)" stroke-width="2"/>
|
||||
<!-- bars: 2024, 2025, 2026 (net) -->
|
||||
<g>
|
||||
<rect x="70" y="242" width="90" height="48" rx="12" fill="rgba(52,199,89,0.18)" stroke="rgba(52,199,89,0.22)"/>
|
||||
<text x="115" y="312" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.55)">2024</text>
|
||||
<text x="115" y="232" text-anchor="middle" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">-0.01万</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="215" y="196" width="90" height="94" rx="12" fill="rgba(255,149,0,0.20)" stroke="rgba(255,149,0,0.24)"/>
|
||||
<text x="260" y="312" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.55)">2025</text>
|
||||
<text x="260" y="186" text-anchor="middle" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">-33.8万</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="360" y="72" width="90" height="218" rx="12" fill="rgba(255,59,48,0.22)" stroke="rgba(255,59,48,0.26)"/>
|
||||
<text x="405" y="312" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.55)">2026</text>
|
||||
<text x="405" y="62" text-anchor="middle" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">-182.9万</text>
|
||||
</g>
|
||||
<text x="28" y="336" font-size="12" font-weight="700" fill="rgba(0,0,0,0.48)">注:2026 异常值需先做短信噪音剔除</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 6: 账户结构 -->
|
||||
<div class="slide" id="slide-6" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Accounts</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">💳</span>
|
||||
<span class="tag">钱主要在哪两条线</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>收入结构:<span>中信银行-0405 占 50.6%</span>,支付宝占 49.1%。</p>
|
||||
<p>大白话:公司收入基本就是<span>两条腿走路</span>。</p>
|
||||
<p>支出结构里:<span>“其他”非常大</span> → 先当“待核对”。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ 财务总监盯盘顺序:先盯“现金进出最大的账户”,也就是中信0405;其次才是零碎账户。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:两条腿 + 饼图 -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">收入占比(两条腿)</text>
|
||||
<circle cx="160" cy="190" r="90" fill="none" stroke="rgba(0,0,0,0.08)" stroke-width="22"/>
|
||||
<circle cx="160" cy="190" r="90" fill="none" stroke="rgba(10,132,255,0.62)" stroke-width="22"
|
||||
stroke-dasharray="283 565" stroke-linecap="round" transform="rotate(-90 160 190)"/>
|
||||
<circle cx="160" cy="190" r="90" fill="none" stroke="rgba(52,199,89,0.58)" stroke-width="22"
|
||||
stroke-dasharray="282 565" stroke-linecap="round" transform="rotate(12 160 190)"/>
|
||||
<text x="160" y="196" text-anchor="middle" font-size="18" font-weight="900" fill="rgba(0,0,0,0.76)">≈ 50 / 50</text>
|
||||
<text x="160" y="220" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">中信0405 / 支付宝</text>
|
||||
|
||||
<rect x="285" y="108" width="206" height="64" rx="16" fill="rgba(10,132,255,0.12)" stroke="rgba(10,132,255,0.18)"/>
|
||||
<text x="305" y="140" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">中信银行-0405</text>
|
||||
<text x="305" y="162" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">收入 111.3 万</text>
|
||||
|
||||
<rect x="285" y="186" width="206" height="64" rx="16" fill="rgba(52,199,89,0.12)" stroke="rgba(52,199,89,0.18)"/>
|
||||
<text x="305" y="218" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">支付宝</text>
|
||||
<text x="305" y="240" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">收入 108.0 万</text>
|
||||
|
||||
<text x="285" y="292" font-size="12" font-weight="800" fill="rgba(0,0,0,0.46)">支出里“其他”先当待核对项</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 7: 中信0405体检 -->
|
||||
<div class="slide" id="slide-7" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">CITIC 0405</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🏦</span>
|
||||
<span class="tag">卡卡猫 = 中信银行-0405</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>收入:<span class="mono">1,112,568.09</span>(约 111.3 万)</p>
|
||||
<p>支出:<span class="mono">1,013,829.59</span>(约 101.4 万)</p>
|
||||
<p>净额:<span class="mono">+98,738.50</span>(约 +9.9 万)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
CFO 大白话:这户总体是“能自己养活自己”的,但月度上会出现<b>回款空窗期</b>,需要现金阈值和预算上限。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:月度风险提示 -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">月度现金流(公司收支表)</text>
|
||||
<rect x="28" y="72" width="464" height="78" rx="18" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="105" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">2025-12:净 +18.08 万</text>
|
||||
<text x="48" y="130" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">回款 30.05 万 / 支出 11.97 万</text>
|
||||
|
||||
<rect x="28" y="168" width="464" height="78" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="48" y="201" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">2026-01:净 -17.99 万</text>
|
||||
<text x="48" y="226" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">无收入 / 支出 17.99 万(现金压力月)</text>
|
||||
|
||||
<rect x="28" y="264" width="464" height="78" rx="18" fill="rgba(255,59,48,0.08)" stroke="rgba(255,59,48,0.14)"/>
|
||||
<text x="48" y="297" font-size="14" font-weight="900" fill="rgba(0,0,0,0.72)">建议:设“现金红线”</text>
|
||||
<text x="48" y="322" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">例如:余额低于 2 个月固定成本 → 自动降本 + 推回款</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 8: 支付宝体检 -->
|
||||
<div class="slide" id="slide-8" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Alipay</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">📲</span>
|
||||
<span class="tag">支付宝这条线</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>收入:<span class="mono">1,080,038.40</span></p>
|
||||
<p>支出:<span class="mono">1,070,871.17</span></p>
|
||||
<p>净额:<span class="mono">+9,167.23</span></p>
|
||||
<p>手续费:<span>约 2,154 元</span>(属于“交易成本”)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ 大白话:支付宝整体“基本打平”,它更像一个收款/分发通道,不是亏损黑洞。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:支付与手续费 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<rect x="70" y="78" width="280" height="110" rx="18" fill="rgba(10,132,255,0.12)" stroke="rgba(10,132,255,0.18)"/>
|
||||
<text x="95" y="120" font-size="16" font-weight="900" fill="rgba(0,0,0,0.72)">交易</text>
|
||||
<text x="95" y="152" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">收入 ≈ 108.0 万</text>
|
||||
<text x="95" y="174" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">支出 ≈ 107.1 万</text>
|
||||
|
||||
<rect x="70" y="224" width="280" height="110" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="95" y="266" font-size="16" font-weight="900" fill="rgba(0,0,0,0.72)">手续费</text>
|
||||
<text x="95" y="298" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">≈ 2,154 元</text>
|
||||
|
||||
<text x="210" y="372" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.48)">能接受的成本 ≠ 异常支出</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 9: 支出Top与异常 -->
|
||||
<div class="slide" id="slide-9" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Expense Red Flags</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🚨</span>
|
||||
<span class="tag">支出 Top 的“真假判断”</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>看到这些就先别慌:<span>它们很可能不是“真支出”</span>。</p>
|
||||
<p>例子:<span>“授予额度 398000”</span>、<span>“剩余额度 0.17 元”</span>、<span>营销提醒</span>。</p>
|
||||
<p>我的建议:<span>把“短信提醒类”单独一栏,别和真实流水混在一起</span>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
🧹 下一次迭代:给短信解析加“过滤规则”(额度/授信/提醒/预测/账单非交易),让报表更接近真实。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:红旗清单 -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">容易误判的短信关键词(建议过滤)</text>
|
||||
<g>
|
||||
<rect x="28" y="72" width="464" height="54" rx="16" fill="rgba(255,59,48,0.10)" stroke="rgba(255,59,48,0.16)"/>
|
||||
<text x="48" y="104" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">授予 / 额度 / 预授信 / 可用额度 / 剩余可用额度</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="28" y="138" width="464" height="54" rx="16" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="48" y="170" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">提醒 / 通知 / 预测 / 将于到期 / 即将到期</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="28" y="204" width="464" height="54" rx="16" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="48" y="236" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">账单:应还 / 最低还款 / 本期无需还款(非交易)</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="28" y="270" width="464" height="70" rx="16" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="302" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">最终目标:把“真实交易”与“提醒噪音”分两张表</text>
|
||||
<text x="48" y="326" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">这样你才能拿报表做决策(降本/预算/回款/投资)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 10: CFO三件事 -->
|
||||
<div class="slide" id="slide-10" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">CFO Playbook</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🧩</span>
|
||||
<span class="tag">先保命,再增长</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>第 1 件:<span>现金能撑多久</span>(按“固定成本/月”算)。</p>
|
||||
<p>第 2 件:<span>支出有没有上限</span>(预算 + 审批)。</p>
|
||||
<p>第 3 件:<span>回款有没有节奏</span>(每周盯应收)。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ 大白话:财务总监最怕的不是“利润低”,是“现金断”。现金断了,业务再好也没用。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:仪表盘 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="40" y="66" font-size="16" font-weight="900" fill="rgba(0,0,0,0.72)">每周必看的 3 个仪表</text>
|
||||
<rect x="40" y="92" width="340" height="84" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="64" y="126" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">现金余粮</text>
|
||||
<text x="64" y="152" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">余额 / 月固定成本 = 还能活几个月</text>
|
||||
|
||||
<rect x="40" y="192" width="340" height="84" rx="18" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="64" y="226" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">预算执行</text>
|
||||
<text x="64" y="252" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">本月已花 / 预算上限(超了立刻刹车)</text>
|
||||
|
||||
<rect x="40" y="292" width="340" height="84" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="64" y="326" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">应收回款</text>
|
||||
<text x="64" y="352" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">本周应收 / 已收 / 逾期(谁没付)</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 11: 30天行动 -->
|
||||
<div class="slide" id="slide-11" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">30-Day Plan</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🗓️</span>
|
||||
<span class="tag">30 天迭代计划</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>第 1 周:<span>把短信噪音剔除</span>(额度/提醒/营销)→ 报表不再“虚胖”。</p>
|
||||
<p>第 2 周:<span>建立预算科目</span>(工资/税/云/房租/外包/营销)。</p>
|
||||
<p>第 3 周:<span>做 13 周现金预测</span>(每周滚动)。</p>
|
||||
<p>第 4 周:<span>固定仪表盘</span>(每周 10 分钟看一次)。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ 你最终要的是“自动化财务雷达”:一眼发现异常,一键追溯明细。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:路线图 -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">从“报表”升级到“财务系统”</text>
|
||||
<path d="M56 100 C160 60, 260 140, 360 100 C420 80, 450 120, 468 150" stroke="rgba(0,0,0,0.18)" stroke-width="6" fill="none" stroke-linecap="round"/>
|
||||
<circle cx="56" cy="100" r="10" fill="rgba(10,132,255,0.70)"/>
|
||||
<circle cx="186" cy="86" r="10" fill="rgba(52,199,89,0.70)"/>
|
||||
<circle cx="306" cy="126" r="10" fill="rgba(255,149,0,0.75)"/>
|
||||
<circle cx="468" cy="150" r="10" fill="rgba(255,59,48,0.70)"/>
|
||||
<text x="40" y="142" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">W1</text>
|
||||
<text x="170" y="128" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">W2</text>
|
||||
<text x="292" y="168" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">W3</text>
|
||||
<text x="452" y="192" font-size="12" font-weight="900" fill="rgba(0,0,0,0.62)">W4</text>
|
||||
|
||||
<rect x="28" y="210" width="464" height="120" rx="18" fill="rgba(255,255,255,0.72)" stroke="rgba(255,255,255,0.92)"/>
|
||||
<text x="48" y="242" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">W1 清口径</text>
|
||||
<text x="48" y="266" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">过滤短信噪音,真实交易单独统计</text>
|
||||
<text x="48" y="294" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">W2 建预算</text>
|
||||
<text x="160" y="294" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">W3 做预测</text>
|
||||
<text x="284" y="294" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">W4 固仪表</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 12: 结尾 -->
|
||||
<div class="slide" id="slide-12" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 56px 72px; text-align:center; width: 980px;">
|
||||
<span class="icon">✅</span>
|
||||
<h1 class="text-dark" style="margin: 18px 0 10px;">最后一句大白话</h1>
|
||||
<p class="text-muted" style="font-size: 22px; font-weight: 800; line-height: 1.6;">
|
||||
报表不是用来“看热闹”的,<br>
|
||||
是用来“提前发现风险、提前踩刹车、提前安排回款”的。
|
||||
</p>
|
||||
<div style="display:flex; justify-content:center; gap:12px; margin-top: 26px; flex-wrap: wrap;">
|
||||
<span class="pill">🧾 口径干净</span>
|
||||
<span class="pill">💳 账户清楚</span>
|
||||
<span class="pill">🧠 结论能落地</span>
|
||||
<span class="pill">📈 每周滚动预测</span>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 18px;">下一步:我可以继续把“短信噪音过滤规则”做成自动更新,让 2026 年异常支出回归真实。</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
449
03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html
Normal file
449
03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html
Normal file
@@ -0,0 +1,449 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, height=720">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
.slide {
|
||||
width: 1280px; height: 720px;
|
||||
display: flex; flex-direction: column;
|
||||
padding: 64px 80px;
|
||||
background: linear-gradient(160deg, #F5F5FA 0%, #EBEBF0 35%, #F0F0F5 100%);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.slide::before {
|
||||
content: ''; position: absolute; top: -40%; right: -25%;
|
||||
width: 70%; height: 90%;
|
||||
background: radial-gradient(ellipse, rgba(0,122,255,0.10) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.slide::after {
|
||||
content: ''; position: absolute; bottom: -45%; left: -22%;
|
||||
width: 70%; height: 95%;
|
||||
background: radial-gradient(ellipse, rgba(52,199,89,0.10) 0%, transparent 58%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.glass {
|
||||
background: rgba(255,255,255,0.60);
|
||||
backdrop-filter: blur(48px); -webkit-backdrop-filter: blur(48px);
|
||||
border: 1px solid rgba(255,255,255,0.72);
|
||||
border-radius: 28px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.04), 0 0 0 1px rgba(255,255,255,0.8) inset;
|
||||
}
|
||||
.glass-strong {
|
||||
background: rgba(255,255,255,0.74);
|
||||
backdrop-filter: blur(56px); -webkit-backdrop-filter: blur(56px);
|
||||
border: 1px solid rgba(255,255,255,0.78);
|
||||
border-radius: 32px;
|
||||
box-shadow: 0 12px 40px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.9) inset;
|
||||
}
|
||||
.text-dark { color: #1D1D1F; }
|
||||
.text-muted { color: #8E8E93; }
|
||||
h1 { font-size: 52px; font-weight: 900; letter-spacing: -0.03em; }
|
||||
h2 { font-size: 18px; font-weight: 900; letter-spacing: 0.12em; text-transform: uppercase; color: #007AFF; }
|
||||
.kicker { font-size: 14px; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(0,0,0,0.45); }
|
||||
.row { display: flex; gap: 36px; }
|
||||
.col { display: flex; flex-direction: column; gap: 18px; }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 10px;
|
||||
padding: 10px 14px; border-radius: 999px;
|
||||
background: rgba(0,122,255,0.10);
|
||||
color: #0B5ED7; font-weight: 800; font-size: 14px;
|
||||
border: 1px solid rgba(0,122,255,0.14);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tag {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
font-size: 14px; font-weight: 800;
|
||||
color: rgba(0,0,0,0.58);
|
||||
padding: 8px 12px; border-radius: 12px;
|
||||
background: rgba(255,255,255,0.55);
|
||||
border: 1px solid rgba(255,255,255,0.7);
|
||||
}
|
||||
.icon { font-size: 42px; line-height: 1; }
|
||||
.small { font-size: 16px; line-height: 1.7; color: rgba(0,0,0,0.68); }
|
||||
.list p { font-size: 20px; line-height: 1.85; color: #1D1D1F; }
|
||||
.list p span { color: #8E8E93; font-weight: 700; }
|
||||
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.metric { padding: 18px 20px; border-radius: 18px; background: rgba(255,255,255,0.62); border: 1px solid rgba(255,255,255,0.75); }
|
||||
.metric .v { font-size: 28px; font-weight: 950; color: #1D1D1F; letter-spacing: -0.02em; }
|
||||
.metric .k { font-size: 13px; font-weight: 900; color: rgba(0,0,0,0.50); letter-spacing: 0.14em; text-transform: uppercase; margin-top: 6px; }
|
||||
.img-card { width: 420px; height: 420px; border-radius: 22px; overflow: hidden; padding: 16px; }
|
||||
.img-card.wide { width: 520px; height: 360px; }
|
||||
.note { padding: 14px 16px; border-radius: 16px; background: rgba(255,149,0,0.12); border: 1px solid rgba(255,149,0,0.18); color: rgba(0,0,0,0.72); font-weight: 700; }
|
||||
.ok { background: rgba(52,199,89,0.12); border-color: rgba(52,199,89,0.18); }
|
||||
.bad { background: rgba(255,59,48,0.10); border-color: rgba(255,59,48,0.16); }
|
||||
.mono { font-variant-numeric: tabular-nums; }
|
||||
table { width: 100%; border-collapse: separate; border-spacing: 0 10px; }
|
||||
td { padding: 12px 14px; font-size: 18px; color: rgba(0,0,0,0.78); }
|
||||
.trow { background: rgba(255,255,255,0.62); border: 1px solid rgba(255,255,255,0.78); border-radius: 16px; }
|
||||
.trow td:first-child { border-top-left-radius: 16px; border-bottom-left-radius: 16px; font-weight: 900; }
|
||||
.trow td:last-child { border-top-right-radius: 16px; border-bottom-right-radius: 16px; text-align: right; font-weight: 950; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Slide 1: 封面 -->
|
||||
<div class="slide" id="slide-1" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 56px 72px; text-align:center; width: 980px;">
|
||||
<div class="kicker" style="margin-bottom: 10px;">Monthly CFO Report · Plain Talk</div>
|
||||
<h1 class="text-dark" style="margin-bottom: 18px;">2026年1月 · 公司财务月报</h1>
|
||||
<p class="text-muted" style="font-size: 22px; font-weight: 800;">十页左右|把“钱从哪来、花到哪去、下月怎么管”讲明白</p>
|
||||
<div style="display:flex; justify-content:center; gap:12px; margin-top: 26px; flex-wrap: wrap;">
|
||||
<span class="pill">🏦 卡卡猫·中信0405</span>
|
||||
<span class="pill">🧾 支付宝·企业</span>
|
||||
<span class="pill">🌊 芸归喜·网商9532</span>
|
||||
<span class="pill">📩 短信口径</span>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 18px;">数据来源:`2026年1月财务报表_完整表格.md`(生成:2026-02-08)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: 本月一句话 + 关键数字 -->
|
||||
<div class="slide" id="slide-2" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Executive Summary</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🧠</span>
|
||||
<span class="tag">一句话结论</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>1月:公司账户核心结论就是一句话:<span>卡卡猫(中信0405)单月净流出 17.99 万</span>。</p>
|
||||
<p>支付宝企业本月是<span>小幅净流入 +240.71</span>;芸归喜只有<span>收入 5.94</span>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ CFO 大白话:这月不是“赚不赚钱”的问题,是“现金流怎么扛”的问题。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">本月三条线(净额)</text>
|
||||
<rect x="28" y="70" width="464" height="76" rx="18" fill="rgba(255,59,48,0.10)" stroke="rgba(255,59,48,0.16)"/>
|
||||
<text x="48" y="104" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">卡卡猫(中信0405)</text>
|
||||
<text x="48" y="130" font-size="14" font-weight="900" fill="rgba(0,0,0,0.56)">净 -179,853.23</text>
|
||||
|
||||
<rect x="28" y="158" width="464" height="76" rx="18" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="192" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">支付宝(企业)</text>
|
||||
<text x="48" y="218" font-size="14" font-weight="900" fill="rgba(0,0,0,0.56)">净 +240.71</text>
|
||||
|
||||
<rect x="28" y="246" width="464" height="76" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="48" y="280" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">短信口径(实时)</text>
|
||||
<text x="48" y="306" font-size="14" font-weight="900" fill="rgba(0,0,0,0.56)">净 +3,715</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 3: 口径说明与待补项 -->
|
||||
<div class="slide" id="slide-3" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Scope</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🧾</span>
|
||||
<span class="tag">这月报表怎么用</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>我们优先把“可确认”的钱算清楚:<span>卡卡猫中信0405</span>、<span>支付宝企业</span>、<span>芸归喜网商9532</span>。</p>
|
||||
<p>短信口径是“实时提醒”:<span>更像雷达</span>,不是最终账。</p>
|
||||
<p>腾讯云消费:<span>本月待补</span>(通常次月出账单)。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
⚠️ 待补项(本月):云消费(腾讯云)、飞书报销/付款明细。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.38)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="36" y="66" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">本月数据来源</text>
|
||||
<rect x="36" y="92" width="348" height="84" rx="18" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="60" y="126" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">重点机构交易明细</text>
|
||||
<text x="60" y="152" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">卡卡猫 / 芸归喜</text>
|
||||
|
||||
<rect x="36" y="192" width="348" height="84" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="60" y="226" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">支付宝 API</text>
|
||||
<text x="60" y="252" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">企业号 signcustomer</text>
|
||||
|
||||
<rect x="36" y="292" width="348" height="92" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="60" y="326" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">iPhone 短信</text>
|
||||
<text x="60" y="352" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">实时提醒(需过滤噪音)</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 4: 总览表(本月) -->
|
||||
<div class="slide" id="slide-4" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Overview</h2>
|
||||
<div class="row" style="align-items:flex-start;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 26px 28px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 10px;">
|
||||
<span class="icon">📋</span>
|
||||
<span class="tag">总览表(大白话)</span>
|
||||
</div>
|
||||
<table>
|
||||
<tr class="trow"><td>短信口径(实时)</td><td class="mono">净 +3,715</td></tr>
|
||||
<tr class="trow"><td>卡卡猫(中信0405)</td><td class="mono" style="color:#FF3B30;">净 -179,853.23</td></tr>
|
||||
<tr class="trow"><td>芸归喜(网商9532)</td><td class="mono">收入 5.94</td></tr>
|
||||
<tr class="trow"><td>支付宝(企业)</td><td class="mono">净 +240.71</td></tr>
|
||||
<tr class="trow"><td>飞书工资(参考)</td><td class="mono">65,500</td></tr>
|
||||
<tr class="trow"><td>云消费(腾讯云)</td><td class="mono">待补</td></tr>
|
||||
</table>
|
||||
<div class="small" style="margin-top: 10px;">
|
||||
大白话:<b>这月现金压力主要来自卡卡猫</b>;支付宝这条线是“小打小闹”;短信口径是“雷达提示”。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">现金流“重心”</text>
|
||||
<rect x="28" y="70" width="464" height="112" rx="22" fill="rgba(255,59,48,0.10)" stroke="rgba(255,59,48,0.16)"/>
|
||||
<text x="48" y="112" font-size="18" font-weight="950" fill="rgba(0,0,0,0.76)">重心:卡卡猫中信0405</text>
|
||||
<text x="48" y="142" font-size="13" font-weight="900" fill="rgba(0,0,0,0.52)">本月净流出 17.99 万(需要预算上限 + 回款节奏)</text>
|
||||
|
||||
<rect x="28" y="198" width="464" height="132" rx="22" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="48" y="240" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">小项:支付宝 / 芸归喜 / 短信</text>
|
||||
<text x="48" y="268" font-size="13" font-weight="900" fill="rgba(0,0,0,0.52)">这些更适合做“监控与预警”,不是现金流主引擎</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 5: 卡卡猫(中信0405) -->
|
||||
<div class="slide" id="slide-5" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">CITIC 0405</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🏦</span>
|
||||
<span class="tag">卡卡猫(中信0405)</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>收入:<span class="mono">0</span>(本月无转入)</p>
|
||||
<p>支出:<span class="mono">179,853.23</span></p>
|
||||
<p>净现金流:<span class="mono" style="color:#FF3B30;">-179,853.23</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
CFO 大白话:<b>没有收入的月份</b>,支出再“合理”也会把现金吃掉。这个月就是典型“现金压力月”。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">支出构成(节选)</text>
|
||||
<rect x="28" y="70" width="464" height="56" rx="16" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="48" y="104" font-size="14" font-weight="950" fill="rgba(0,0,0,0.74)">厦门存客宝:60,000</text>
|
||||
<rect x="28" y="136" width="464" height="56" rx="16" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="170" font-size="14" font-weight="950" fill="rgba(0,0,0,0.74)">苏培玉:26,660.74</text>
|
||||
<rect x="28" y="202" width="464" height="56" rx="16" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="48" y="236" font-size="14" font-weight="950" fill="rgba(0,0,0,0.74)">好帖云:16,100</text>
|
||||
<rect x="28" y="268" width="464" height="68" rx="16" fill="rgba(255,59,48,0.08)" stroke="rgba(255,59,48,0.14)"/>
|
||||
<text x="48" y="302" font-size="13" font-weight="900" fill="rgba(0,0,0,0.72)">缴税(TIPS)+ 银行费 + 多笔打款</text>
|
||||
<text x="48" y="324" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">典型:税费、工资/打款、服务费、供应商付款</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 6: 支付宝企业 -->
|
||||
<div class="slide" id="slide-6" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Alipay (Enterprise)</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">📲</span>
|
||||
<span class="tag">支付宝企业号</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>收入:<span class="mono">1,301.90</span>(5 笔)</p>
|
||||
<p>支出:<span class="mono">1,061.19</span>(10 笔)</p>
|
||||
<p>净额:<span class="mono">+240.71</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ 大白话:支付宝这条线本月是“<b>小幅净流入</b>”,它更像收款/分发通道。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="36" y="66" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">一月特点</text>
|
||||
<rect x="36" y="92" width="348" height="92" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="60" y="126" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">收入:暗黑4 / Cursor / 充值</text>
|
||||
<text x="60" y="152" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">小额多笔,偏零售型</text>
|
||||
|
||||
<rect x="36" y="206" width="348" height="92" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="60" y="240" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">支出:服务费/转账/平台消费</text>
|
||||
<text x="60" y="266" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">更适合做“费用科目化”</text>
|
||||
|
||||
<text x="210" y="356" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.48)">建议:把“平台消费”单独列一科目</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 7: 芸归喜 & 飞书工资 -->
|
||||
<div class="slide" id="slide-7" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Small Lines</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🌊</span>
|
||||
<span class="tag">芸归喜 & 飞书</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>芸归喜(网商9532):<span class="mono">收入 5.94</span>(体量很小)。</p>
|
||||
<p>飞书工资(参考):<span class="mono">65,500</span>(若属公司刚性成本,需纳入预算)。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
CFO 大白话:小账户别花太多精力,<b>重点是把刚性成本“预算化”</b>,避免现金月爆雷。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">刚性成本提醒</text>
|
||||
<rect x="28" y="70" width="464" height="120" rx="22" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="48" y="108" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">工资/税/云/房租/外包</text>
|
||||
<text x="48" y="136" font-size="13" font-weight="900" fill="rgba(0,0,0,0.52)">这些最好在月初就“先占用预算”</text>
|
||||
<text x="48" y="162" font-size="13" font-weight="900" fill="rgba(0,0,0,0.52)">剩下的钱才是你能自由支配的</text>
|
||||
|
||||
<rect x="28" y="206" width="464" height="124" rx="22" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="244" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">建议:设“现金红线”</text>
|
||||
<text x="48" y="272" font-size="13" font-weight="900" fill="rgba(0,0,0,0.52)">比如:现金余额 < 2个月固定成本 → 自动降本/停非必要支出</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 8: 短信口径怎么用 -->
|
||||
<div class="slide" id="slide-8" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">SMS Radar</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">📩</span>
|
||||
<span class="tag">短信口径(实时)</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>本月短信口径:<span>收入 4,885 / 支出 1,170 / 净 3,715</span>。</p>
|
||||
<p>大白话:短信更像<span>“预警雷达”</span>,帮你发现“突然发生了什么”。</p>
|
||||
<p>但它不适合当最终账:<span>可能混入提醒/营销/额度</span>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note" style="margin-top: 14px;">
|
||||
✅ 用法建议:短信用来“抓异常”;最终对账以重点机构交易明细 & 银行对账单为准。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="rgba(255,255,255,0.40)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="36" y="66" font-size="16" font-weight="950" fill="rgba(0,0,0,0.76)">短信的正确姿势</text>
|
||||
<rect x="36" y="92" width="348" height="86" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="60" y="126" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">✅ 看“有没有发生”</text>
|
||||
<text x="60" y="152" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">比如:转入/转出/缴税/大额打款</text>
|
||||
|
||||
<rect x="36" y="192" width="348" height="86" rx="18" fill="rgba(255,59,48,0.08)" stroke="rgba(255,59,48,0.14)"/>
|
||||
<text x="60" y="226" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">❌ 别当“最终账”</text>
|
||||
<text x="60" y="252" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">提醒/营销/额度会把数字带偏</text>
|
||||
|
||||
<text x="210" y="356" text-anchor="middle" font-size="12" font-weight="800" fill="rgba(0,0,0,0.48)">一句话:短信抓异常,明细做结账</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 9: 风险与建议 -->
|
||||
<div class="slide" id="slide-9" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 18px;">Risks & Actions</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 28px 32px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🚨</span>
|
||||
<span class="tag">本月风险点</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>风险 1:<span>中信0405 无收入但支出 17.99 万</span> → 现金压力月。</p>
|
||||
<p>风险 2:<span>云消费待补</span> → 月末可能再来一刀。</p>
|
||||
<p>风险 3:<span>工资/税/固定服务费</span> → 刚性成本必须预算化。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-top: 14px;">
|
||||
✅ CFO 行动:下月把“预算上限 + 回款节奏 + 现金红线”三件事落到制度里。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="rgba(255,255,255,0.42)"/>
|
||||
<g font-family="Inter, -apple-system, sans-serif">
|
||||
<text x="28" y="42" font-size="14" font-weight="900" fill="rgba(0,0,0,0.70)">下月三条硬动作</text>
|
||||
<rect x="28" y="70" width="464" height="76" rx="18" fill="rgba(10,132,255,0.10)" stroke="rgba(10,132,255,0.16)"/>
|
||||
<text x="48" y="104" font-size="15" font-weight="950" fill="rgba(0,0,0,0.76)">1) 预算上限</text>
|
||||
<text x="48" y="130" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">工资/税/云/外包/营销:每科目一个上限</text>
|
||||
|
||||
<rect x="28" y="160" width="464" height="76" rx="18" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.16)"/>
|
||||
<text x="48" y="194" font-size="15" font-weight="950" fill="rgba(0,0,0,0.76)">2) 回款节奏</text>
|
||||
<text x="48" y="220" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">每周盯“应收/已收/逾期”,减少空窗期</text>
|
||||
|
||||
<rect x="28" y="250" width="464" height="84" rx="18" fill="rgba(255,149,0,0.10)" stroke="rgba(255,149,0,0.16)"/>
|
||||
<text x="48" y="284" font-size="15" font-weight="950" fill="rgba(0,0,0,0.76)">3) 现金红线</text>
|
||||
<text x="48" y="310" font-size="12" font-weight="800" fill="rgba(0,0,0,0.52)">现金 < 2个月固定成本 → 自动降本/暂停非必要支出</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 10: 结尾与下次迭代 -->
|
||||
<div class="slide" id="slide-10" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 56px 72px; text-align:center; width: 980px;">
|
||||
<span class="icon">✅</span>
|
||||
<h1 class="text-dark" style="margin: 18px 0 10px;">本月最后一句大白话</h1>
|
||||
<p class="text-muted" style="font-size: 22px; font-weight: 850; line-height: 1.6;">
|
||||
1月最关键不是“算得多细”,<br>
|
||||
是先把卡卡猫中信0405的现金流稳住:<br>
|
||||
<b>预算有上限、回款有节奏、现金有红线。</b>
|
||||
</p>
|
||||
<div style="display:flex; justify-content:center; gap:12px; margin-top: 26px; flex-wrap: wrap;">
|
||||
<span class="pill">🧾 云消费补齐</span>
|
||||
<span class="pill">📊 费用科目化</span>
|
||||
<span class="pill">🧠 每周滚动预测</span>
|
||||
<span class="pill">🚨 异常预警</span>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 18px;">下一步迭代:把“云消费(腾讯云)”与“飞书报销/付款”补齐,形成完整月结。</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
423
03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html
Normal file
423
03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html
Normal file
@@ -0,0 +1,423 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, height=720">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Inter', -apple-system, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
.slide {
|
||||
width: 1280px; height: 720px;
|
||||
display: flex; flex-direction: column;
|
||||
padding: 64px 80px;
|
||||
background: linear-gradient(160deg, #F5F5FA 0%, #EBEBF0 35%, #F0F0F5 100%);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.slide::before {
|
||||
content: ''; position: absolute; top: -40%; right: -25%;
|
||||
width: 70%; height: 90%;
|
||||
background: radial-gradient(ellipse, rgba(0,122,255,0.08) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.glass {
|
||||
background: rgba(255,255,255,0.6);
|
||||
backdrop-filter: blur(48px); -webkit-backdrop-filter: blur(48px);
|
||||
border: 1px solid rgba(255,255,255,0.7);
|
||||
border-radius: 28px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.04), 0 0 0 1px rgba(255,255,255,0.8) inset;
|
||||
}
|
||||
.glass-strong {
|
||||
background: rgba(255,255,255,0.72);
|
||||
backdrop-filter: blur(56px); -webkit-backdrop-filter: blur(56px);
|
||||
border: 1px solid rgba(255,255,255,0.75);
|
||||
border-radius: 32px;
|
||||
box-shadow: 0 12px 40px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.9) inset;
|
||||
}
|
||||
.text-dark { color: #1D1D1F; }
|
||||
.text-muted { color: #8E8E93; }
|
||||
.accent { color: #007AFF; }
|
||||
h1 { font-size: 52px; font-weight: 700; letter-spacing: -0.03em; }
|
||||
h2 { font-size: 18px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: #007AFF; }
|
||||
.kicker { font-size: 14px; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(0,0,0,0.45); }
|
||||
.row { display: flex; gap: 36px; }
|
||||
.col { display: flex; flex-direction: column; gap: 18px; }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 10px;
|
||||
padding: 10px 14px; border-radius: 999px;
|
||||
background: rgba(0,122,255,0.10);
|
||||
color: #0B5ED7; font-weight: 600; font-size: 14px;
|
||||
border: 1px solid rgba(0,122,255,0.12);
|
||||
}
|
||||
.list p { font-size: 20px; line-height: 1.8; color: #1D1D1F; }
|
||||
.list p span { color: #8E8E93; }
|
||||
.icon { font-size: 42px; line-height: 1; }
|
||||
.img-card { width: 420px; height: 420px; border-radius: 22px; overflow: hidden; }
|
||||
.img-card.wide { width: 520px; height: 360px; }
|
||||
.tag {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
font-size: 14px; font-weight: 600;
|
||||
color: rgba(0,0,0,0.55);
|
||||
padding: 8px 12px; border-radius: 12px;
|
||||
background: rgba(255,255,255,0.55);
|
||||
border: 1px solid rgba(255,255,255,0.7);
|
||||
}
|
||||
.small { font-size: 16px; line-height: 1.65; color: rgba(0,0,0,0.65); }
|
||||
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.metric { padding: 18px 20px; border-radius: 18px; background: rgba(255,255,255,0.62); border: 1px solid rgba(255,255,255,0.75); }
|
||||
.metric .v { font-size: 26px; font-weight: 800; color: #1D1D1F; letter-spacing: -0.02em; }
|
||||
.metric .k { font-size: 14px; font-weight: 700; color: rgba(0,0,0,0.50); letter-spacing: 0.12em; text-transform: uppercase; margin-top: 6px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Slide 1: 封面 -->
|
||||
<div class="slide" id="slide-1" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 56px 72px; text-align:center; width: 920px;">
|
||||
<div class="kicker" style="margin-bottom: 10px;">Conversation → SOP → Skill</div>
|
||||
<h1 class="text-dark" style="margin-bottom: 18px;">家里 NAS 对话描述</h1>
|
||||
<p class="text-muted" style="font-size: 22px; font-weight: 600;">DiskStation · Time Machine · 外网挂载 · 自动沉淀</p>
|
||||
<div style="display:flex; justify-content:center; gap:12px; margin-top: 26px;">
|
||||
<span class="pill">💧 诊断</span>
|
||||
<span class="pill">🪙 运维</span>
|
||||
<span class="pill">🌱 产出</span>
|
||||
<span class="pill">🔥 复盘</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: 对话起点(问题) -->
|
||||
<div class="slide" id="slide-2" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 28px;">What Happened</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="glass-strong" style="padding: 34px 40px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">⏱️</span>
|
||||
<span class="tag">对话触发</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>① 时间机器:<span>红点 + 等待首次备份</span></p>
|
||||
<p>② 弹窗:<span>未识别备份磁盘(DiskStation.local)</span></p>
|
||||
<p>③ 诉求:<span>外网挂载 1TB 共享到 Finder「位置」</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass" style="padding: 22px 28px;">
|
||||
<div class="small">关键目标:能自动就自动;做不了就落材料;最后沉淀到 Skill,可复用。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:抽象 NAS + 云 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#0A84FF" stop-opacity="0.18"/>
|
||||
<stop offset="100%" stop-color="#34C759" stop-opacity="0.14"/>
|
||||
</linearGradient>
|
||||
<filter id="s1" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="14" stdDeviation="12" flood-color="#000" flood-opacity="0.10"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="url(#g1)"/>
|
||||
<g filter="url(#s1)">
|
||||
<rect x="72" y="130" width="276" height="170" rx="18" fill="rgba(255,255,255,0.72)" stroke="rgba(255,255,255,0.75)"/>
|
||||
<rect x="98" y="160" width="224" height="20" rx="10" fill="rgba(0,0,0,0.08)"/>
|
||||
<rect x="98" y="194" width="224" height="20" rx="10" fill="rgba(0,0,0,0.08)"/>
|
||||
<rect x="98" y="228" width="224" height="20" rx="10" fill="rgba(0,0,0,0.08)"/>
|
||||
<circle cx="108" cy="276" r="6" fill="#34C759"/>
|
||||
<circle cx="130" cy="276" r="6" fill="#FF9F0A"/>
|
||||
<circle cx="152" cy="276" r="6" fill="#FF453A"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M274 92c18 0 33 14 33 31 0 2 0 4-1 6h4c18 0 33 14 33 31s-15 31-33 31H149c-20 0-36-15-36-34s16-33 36-33c4-18 21-32 40-32 13 0 25 6 33 15 6-9 15-15 27-15z"
|
||||
fill="rgba(255,255,255,0.75)" stroke="rgba(255,255,255,0.72)"/>
|
||||
<text x="210" y="170" text-anchor="middle" font-size="16" font-weight="700" fill="rgba(0,0,0,0.55)">DiskStation ↔ 外网</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 3: 资产与路径(两台 NAS) -->
|
||||
<div class="slide" id="slide-3" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 28px;">Assets Map</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="glass-strong" style="flex:1; padding: 34px 40px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 16px;">
|
||||
<span class="icon">🗺️</span>
|
||||
<span class="tag">公司 / 家里 两套 NAS</span>
|
||||
</div>
|
||||
<div class="grid2">
|
||||
<div class="metric">
|
||||
<div class="v">CKB NAS</div>
|
||||
<div class="k">192.168.1.201</div>
|
||||
<div class="small" style="margin-top:10px;">外网:open.quwanzhi.com</div>
|
||||
<div class="small">Gitea::3000(已穿透)</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="v">家里 DiskStation</div>
|
||||
<div class="k">192.168.110.29</div>
|
||||
<div class="small" style="margin-top:10px;">外网:opennas2.quwanzhi.com</div>
|
||||
<div class="small">Time Machine:共享</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass" style="padding: 18px 22px; margin-top: 16px;">
|
||||
<div class="small">对话中所有“定位/修复/挂载”,都先区分:110 网段=家里;1 网段=公司。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:双节点结构图(SVG) -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g2" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#FF9F0A" stop-opacity="0.12"/>
|
||||
<stop offset="100%" stop-color="#0A84FF" stop-opacity="0.14"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L8,3 L0,6 Z" fill="rgba(0,0,0,0.35)"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="url(#g2)"/>
|
||||
<rect x="48" y="92" width="190" height="176" rx="18" fill="rgba(255,255,255,0.74)"/>
|
||||
<rect x="282" y="92" width="190" height="176" rx="18" fill="rgba(255,255,255,0.74)"/>
|
||||
<text x="143" y="132" text-anchor="middle" font-size="16" font-weight="800" fill="rgba(0,0,0,0.62)">CKB NAS</text>
|
||||
<text x="377" y="132" text-anchor="middle" font-size="16" font-weight="800" fill="rgba(0,0,0,0.62)">家里 NAS</text>
|
||||
<text x="143" y="162" text-anchor="middle" font-size="13" font-weight="700" fill="rgba(0,0,0,0.48)">open.quwanzhi.com</text>
|
||||
<text x="377" y="162" text-anchor="middle" font-size="13" font-weight="700" fill="rgba(0,0,0,0.48)">opennas2.quwanzhi.com</text>
|
||||
<g stroke="rgba(0,0,0,0.35)" stroke-width="2" marker-end="url(#arrow)">
|
||||
<line x1="238" y1="180" x2="282" y2="180"/>
|
||||
<line x1="282" y1="180" x2="238" y2="180"/>
|
||||
</g>
|
||||
<text x="260" y="206" text-anchor="middle" font-size="12" fill="rgba(0,0,0,0.45)">对外入口 / 穿透</text>
|
||||
<g>
|
||||
<rect x="78" y="196" width="130" height="48" rx="14" fill="rgba(0,122,255,0.10)" stroke="rgba(0,122,255,0.14)"/>
|
||||
<text x="143" y="226" text-anchor="middle" font-size="13" font-weight="800" fill="rgba(0,0,0,0.55)">Gitea / 同步</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="312" y="196" width="130" height="48" rx="14" fill="rgba(52,199,89,0.10)" stroke="rgba(52,199,89,0.14)"/>
|
||||
<text x="377" y="226" text-anchor="middle" font-size="13" font-weight="800" fill="rgba(0,0,0,0.55)">Time Machine</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 4: 流程图(核心) -->
|
||||
<div class="slide" id="slide-4" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 22px;">Flowchart</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="col" style="flex:1;">
|
||||
<div class="glass-strong" style="padding: 28px 34px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between;">
|
||||
<span class="icon">🔁</span>
|
||||
<span class="tag">从对话 → SOP → Skill</span>
|
||||
</div>
|
||||
<div class="small" style="margin-top: 12px;">
|
||||
这页是“对话行动链”:诊断 → 修复 → 验证 → 沉淀。后续同类问题直接复用。
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass" style="padding: 18px 22px;">
|
||||
<div class="small">产出物:检测脚本、排查文档、Skill 小节、外网挂载脚本、复盘规则。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card wide" style="padding: 16px;">
|
||||
<!-- 配图:流程图 SVG -->
|
||||
<svg viewBox="0 0 520 360" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#0A84FF" stop-opacity="0.10"/>
|
||||
<stop offset="100%" stop-color="#BF5AF2" stop-opacity="0.10"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow2" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||
<path d="M0,0 L10,6 L0,12 Z" fill="rgba(0,0,0,0.35)"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="520" height="360" rx="18" fill="url(#g3)"/>
|
||||
<!-- nodes -->
|
||||
<g font-family="Inter, -apple-system, sans-serif" font-weight="800">
|
||||
<rect x="48" y="52" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="143" y="86" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">识别问题(对话触发)</text>
|
||||
|
||||
<rect x="282" y="52" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="377" y="86" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">自动检测(脚本)</text>
|
||||
|
||||
<rect x="48" y="136" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="143" y="170" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">Time Machine 修复</text>
|
||||
|
||||
<rect x="282" y="136" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="377" y="170" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">外网挂载(SMB+frp)</text>
|
||||
|
||||
<rect x="48" y="220" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="143" y="254" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">验证(再跑脚本)</text>
|
||||
|
||||
<rect x="282" y="220" width="190" height="56" rx="16" fill="rgba(255,255,255,0.76)"/>
|
||||
<text x="377" y="254" text-anchor="middle" font-size="14" fill="rgba(0,0,0,0.60)">沉淀:文档+Skill</text>
|
||||
</g>
|
||||
|
||||
<!-- arrows -->
|
||||
<g stroke="rgba(0,0,0,0.35)" stroke-width="2.2" marker-end="url(#arrow2)" fill="none">
|
||||
<path d="M238 80 L282 80"/>
|
||||
<path d="M377 108 L377 136"/>
|
||||
<path d="M282 164 L238 164"/>
|
||||
<path d="M143 192 L143 220"/>
|
||||
<path d="M238 248 L282 248"/>
|
||||
</g>
|
||||
|
||||
<g font-family="Inter, -apple-system, sans-serif" font-size="12" fill="rgba(0,0,0,0.48)" font-weight="700">
|
||||
<text x="260" y="70" text-anchor="middle">查IP/端口/状态</text>
|
||||
<text x="377" y="126" text-anchor="middle">错误→按材料</text>
|
||||
<text x="260" y="156" text-anchor="middle">移除→重加</text>
|
||||
<text x="143" y="216" text-anchor="middle">确认红点消失</text>
|
||||
<text x="260" y="240" text-anchor="middle">写入 SOP/Skill</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 5: 自动化产出清单 -->
|
||||
<div class="slide" id="slide-5" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 26px;">Deliverables</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="glass-strong" style="flex:1; padding: 32px 38px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 12px;">
|
||||
<span class="icon">🧰</span>
|
||||
<span class="tag">可复用资产</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>① 检测脚本:<span>time_machine_diskstation_auto.sh</span></p>
|
||||
<p>② 排查文档:<span>Time_Machine_DiskStation_错误排查.md</span></p>
|
||||
<p>③ Skill:<span>群晖NAS管理(含 Time Machine 小节)</span></p>
|
||||
<p>④ 外网挂载:<span>mount_diskstation_1tb.sh + 参考资料</span></p>
|
||||
<p>⑤ 复盘规则:<span>目标每句≤30字+必须含%</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:工具箱 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g4" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#34C759" stop-opacity="0.12"/>
|
||||
<stop offset="100%" stop-color="#0A84FF" stop-opacity="0.12"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="url(#g4)"/>
|
||||
<g>
|
||||
<rect x="76" y="160" width="268" height="168" rx="22" fill="rgba(255,255,255,0.78)" stroke="rgba(255,255,255,0.75)"/>
|
||||
<rect x="130" y="126" width="160" height="60" rx="18" fill="rgba(255,255,255,0.78)" stroke="rgba(255,255,255,0.75)"/>
|
||||
<rect x="152" y="144" width="116" height="24" rx="12" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="110" y="204" width="200" height="18" rx="9" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="110" y="234" width="200" height="18" rx="9" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="110" y="264" width="200" height="18" rx="9" fill="rgba(0,0,0,0.07)"/>
|
||||
<text x="210" y="310" text-anchor="middle" font-size="14" font-weight="800" fill="rgba(0,0,0,0.55)">Scripts · Docs · Skills</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 6: 家里 NAS 关键参数 -->
|
||||
<div class="slide" id="slide-6" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 26px;">Home NAS Snapshot</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="glass-strong" style="flex:1; padding: 32px 38px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 14px;">
|
||||
<span class="icon">🪙</span>
|
||||
<span class="tag">资产摘要</span>
|
||||
</div>
|
||||
<div class="grid2">
|
||||
<div class="metric"><div class="v">DS213j</div><div class="k">MODEL</div></div>
|
||||
<div class="metric"><div class="v">armv7l</div><div class="k">ARCH</div></div>
|
||||
<div class="metric"><div class="v">497MB</div><div class="k">RAM</div></div>
|
||||
<div class="metric"><div class="v">5.4TB</div><div class="k">DISK</div></div>
|
||||
</div>
|
||||
<div class="glass" style="padding: 18px 22px; margin-top: 16px;">
|
||||
<div class="small">开放端口:22 / 80 / 443 / 139 / 445 / 5000 / 5001;对外域名:opennas2.quwanzhi.com。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:指标卡 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#BF5AF2" stop-opacity="0.10"/>
|
||||
<stop offset="100%" stop-color="#FF9F0A" stop-opacity="0.10"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="url(#g5)"/>
|
||||
<g>
|
||||
<rect x="62" y="86" width="296" height="248" rx="22" fill="rgba(255,255,255,0.78)" stroke="rgba(255,255,255,0.75)"/>
|
||||
<text x="210" y="130" text-anchor="middle" font-size="16" font-weight="900" fill="rgba(0,0,0,0.60)">Home NAS</text>
|
||||
<rect x="96" y="154" width="228" height="14" rx="7" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="96" y="182" width="228" height="14" rx="7" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="96" y="210" width="228" height="14" rx="7" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="96" y="238" width="228" height="14" rx="7" fill="rgba(0,0,0,0.07)"/>
|
||||
<rect x="96" y="266" width="228" height="14" rx="7" fill="rgba(0,0,0,0.07)"/>
|
||||
<text x="210" y="314" text-anchor="middle" font-size="13" font-weight="800" fill="rgba(0,0,0,0.52)">Time Machine / SMB / FRP</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 7: 下一步行动(用户可执行) -->
|
||||
<div class="slide" id="slide-7" style="justify-content:center;">
|
||||
<h2 style="margin-bottom: 26px;">Next Actions</h2>
|
||||
<div class="row" style="align-items:center;">
|
||||
<div class="glass-strong" style="flex:1; padding: 32px 38px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 12px;">
|
||||
<span class="icon">▶️</span>
|
||||
<span class="tag">按顺序执行</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<p>① Time Machine:<span>移除“共享”→重新添加</span></p>
|
||||
<p>② 外网挂载:<span>frpc 加 SMB 4452 → Finder ⌘K</span></p>
|
||||
<p>③ 自动化:<span>遇到同类问题直接跑检测脚本</span></p>
|
||||
<p>④ 复盘:<span>对话结尾统一按复盘块输出</span></p>
|
||||
</div>
|
||||
<div class="glass" style="padding: 18px 22px; margin-top: 16px;">
|
||||
<div class="small">提示:Finder 复制文件会显示速率;侧栏固定:挂载后拖到「位置」。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass img-card" style="padding: 16px;">
|
||||
<!-- 配图:箭头与侧栏 -->
|
||||
<svg viewBox="0 0 420 420" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="g6" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#0A84FF" stop-opacity="0.10"/>
|
||||
<stop offset="100%" stop-color="#34C759" stop-opacity="0.10"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow3" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||
<path d="M0,0 L10,6 L0,12 Z" fill="rgba(0,0,0,0.35)"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="420" height="420" rx="18" fill="url(#g6)"/>
|
||||
<g>
|
||||
<rect x="64" y="86" width="292" height="248" rx="22" fill="rgba(255,255,255,0.78)" stroke="rgba(255,255,255,0.75)"/>
|
||||
<rect x="86" y="110" width="86" height="200" rx="16" fill="rgba(0,0,0,0.05)"/>
|
||||
<rect x="182" y="110" width="152" height="38" rx="14" fill="rgba(0,122,255,0.12)"/>
|
||||
<text x="258" y="135" text-anchor="middle" font-size="12" font-weight="900" fill="rgba(0,0,0,0.55)">Finder 位置</text>
|
||||
<rect x="182" y="162" width="152" height="38" rx="14" fill="rgba(52,199,89,0.12)"/>
|
||||
<text x="258" y="187" text-anchor="middle" font-size="12" font-weight="900" fill="rgba(0,0,0,0.55)">DiskStation-1TB</text>
|
||||
<path d="M120 306 C180 320, 240 320, 300 306" stroke="rgba(0,0,0,0.35)" stroke-width="2.4" fill="none" marker-end="url(#arrow3)"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 8: 结尾(对话→复用) -->
|
||||
<div class="slide" id="slide-8" style="justify-content:center; align-items:center;">
|
||||
<div class="glass-strong" style="padding: 46px 60px; width: 980px;">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between;">
|
||||
<span class="icon">🏁</span>
|
||||
<span class="tag">结尾</span>
|
||||
</div>
|
||||
<h1 class="text-dark" style="font-size: 44px; margin: 16px 0 10px;">把一次对话变成可复用系统</h1>
|
||||
<p class="text-muted" style="font-size: 20px; font-weight: 600;">诊断 → 修复 → 验证 → 文档/Skill 沉淀</p>
|
||||
<div class="glass" style="margin-top: 18px; padding: 18px 22px;">
|
||||
<div class="small">PPT 用途:对外汇报、团队交接、下次直接照流程做,不再重复沟通成本。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -63,19 +63,34 @@ def build_ppt(imgs, out_ppt):
|
||||
def main():
|
||||
import argparse
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--html", default="复盘", choices=["复盘", "卡若人设", "纳瓦尔访谈", "天恩乖乖", "今日日志总结"])
|
||||
ap.add_argument("--html", default="复盘", choices=["复盘", "卡若人设", "纳瓦尔访谈", "天恩乖乖", "今日日志总结", "家里NAS对话描述", "公司财务分析", "公司财务月报_2026-01"])
|
||||
args = ap.parse_args()
|
||||
if args.html == "卡若人设":
|
||||
html = BASE / "卡若人设PPT_毛玻璃.html"
|
||||
out_slides = OUT_ROOT / "卡若人设_毛玻璃_slides"
|
||||
out_ppt = OUT_ROOT / "卡若人设介绍_毛玻璃.pptx"
|
||||
max_slides = 5
|
||||
elif args.html == "家里NAS对话描述":
|
||||
html = BASE / "家里NAS对话描述PPT_毛玻璃.html"
|
||||
out_slides = OUT_ROOT / "家里NAS对话描述_毛玻璃_slides"
|
||||
out_ppt = OUT_ROOT / "家里NAS_对话描述_毛玻璃.pptx"
|
||||
max_slides = 8
|
||||
elif args.html == "纳瓦尔访谈":
|
||||
html = BASE / "纳瓦尔访谈PPT_毛玻璃.html"
|
||||
# v2:扩展页数(含方法/问答/流程图/行动清单),避免覆盖旧版
|
||||
out_slides = OUT_ROOT / "纳瓦尔访谈_毛玻璃_slides_v2"
|
||||
out_ppt = OUT_ROOT / "纳瓦尔访谈_读书笔记_毛玻璃_v2.pptx"
|
||||
max_slides = 15
|
||||
elif args.html == "公司财务分析":
|
||||
html = BASE / "公司财务分析PPT_毛玻璃.html"
|
||||
out_slides = OUT_ROOT / "公司财务分析_毛玻璃_slides"
|
||||
out_ppt = OUT_ROOT / "公司财务_多维分析_CFO大白话_毛玻璃.pptx"
|
||||
max_slides = 12
|
||||
elif args.html == "公司财务月报_2026-01":
|
||||
html = BASE / "公司财务月报_2026年1月PPT_毛玻璃.html"
|
||||
out_slides = OUT_ROOT / "公司财务月报_2026-01_毛玻璃_slides"
|
||||
out_ppt = OUT_ROOT / "公司财务月报_2026年1月_CFO大白话_毛玻璃.pptx"
|
||||
max_slides = 10
|
||||
elif args.html == "天恩乖乖":
|
||||
html = BASE / "天恩乖乖PPT_毛玻璃.html"
|
||||
out_slides = TIANEN_DIR / "乖乖_毛玻璃_slides"
|
||||
|
||||
@@ -15,6 +15,62 @@ uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
- `OPENAI_API_KEY`:OpenAI 或兼容 API 的密钥,配置后使用真实 LLM 生成回复。
|
||||
- `OPENAI_API_BASE`:兼容接口地址,默认 `https://api.openai.com/v1`。
|
||||
- `OPENAI_MODEL`:模型名,默认 `gpt-4o-mini`。
|
||||
- `KARUO_GATEWAY_CONFIG`:网关配置路径(默认 `config/gateway.yaml`)。
|
||||
- `KARUO_GATEWAY_SALT`:部门 Key 的 salt(用于 sha256 校验;不写入仓库)。
|
||||
|
||||
## 部门/科室鉴权与白名单(推荐启用)
|
||||
|
||||
网关支持“每部门一个 Key + 技能白名单”,用于:
|
||||
|
||||
- 科室/部门直接调用接口,不互相影响
|
||||
- 外网暴露时避免“全能力裸奔”
|
||||
- 能按部门做限流/审计日志
|
||||
|
||||
### 1) 准备配置文件
|
||||
|
||||
从示例复制一份(`gateway.yaml` 建议不要提交到仓库):
|
||||
|
||||
- `config/gateway.example.yaml` → `config/gateway.yaml`
|
||||
|
||||
### 2) 准备 salt(只在环境变量)
|
||||
|
||||
在运行环境里设置:
|
||||
|
||||
```bash
|
||||
export KARUO_GATEWAY_SALT="一个足够长的随机字符串"
|
||||
```
|
||||
|
||||
### 3) 生成部门 Key 与 hash
|
||||
|
||||
```bash
|
||||
python tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科"
|
||||
```
|
||||
|
||||
把输出里的 `api_key_sha256` 写入 `config/gateway.yaml` 对应 tenant;明文 `dept_key` 只出现一次,保存到部门系统的安全配置里。
|
||||
|
||||
### 4) 调用方式
|
||||
|
||||
#### 4.1 /v1/chat
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://127.0.0.1:8000/v1/chat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Karuo-Api-Key: <dept_key>" \
|
||||
-d '{"prompt":"你的问题"}'
|
||||
```
|
||||
|
||||
#### 4.2 /v1/skills(部门自查)
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8000/v1/skills" \
|
||||
-H "X-Karuo-Api-Key: <dept_key>"
|
||||
```
|
||||
|
||||
#### 4.3 /v1/health
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8000/v1/health"
|
||||
```
|
||||
|
||||
## 外网暴露
|
||||
|
||||
@@ -28,6 +84,7 @@ uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
```bash
|
||||
curl -s -X POST "https://YOUR_DOMAIN/v1/chat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Karuo-Api-Key: <dept_key>" \
|
||||
-d '{"prompt":"你的问题"}' | jq -r '.reply'
|
||||
```
|
||||
|
||||
|
||||
40
运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml
Normal file
40
运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
version: 1
|
||||
|
||||
auth:
|
||||
# 外部调用请求头名:每个部门一个 Key
|
||||
header_name: X-Karuo-Api-Key
|
||||
# 只存 hash,不存明文 key;salt 从环境变量读取
|
||||
salt_env: KARUO_GATEWAY_SALT
|
||||
|
||||
tenants:
|
||||
- id: finance
|
||||
name: 财务科
|
||||
# sha256(dept_key + salt) 的 hex;用 tools/generate_dept_key.py 生成
|
||||
api_key_sha256: "REPLACE_ME"
|
||||
# 允许调用的技能:支持填技能ID(如 E05a)或 SKILL 路径(如 05_卡土(土)/.../SKILL.md)
|
||||
allowed_skills:
|
||||
- E05a
|
||||
- M07
|
||||
limits:
|
||||
rpm: 60
|
||||
max_prompt_chars: 12000
|
||||
|
||||
skills:
|
||||
registry_path: SKILL_REGISTRY.md
|
||||
match_strategy: trigger_contains
|
||||
# 未匹配到技能时的策略:deny | allow_general
|
||||
on_no_match: deny
|
||||
|
||||
llm:
|
||||
provider: openai_compatible
|
||||
api_key_env: OPENAI_API_KEY
|
||||
api_base_env: OPENAI_API_BASE
|
||||
model_env: OPENAI_MODEL
|
||||
timeout_seconds: 60
|
||||
max_tokens: 2000
|
||||
|
||||
logging:
|
||||
enabled: true
|
||||
# 既可填绝对路径,也可填相对仓库根目录的路径
|
||||
path: 运营中枢/工作台/karuo_ai_gateway_access.jsonl
|
||||
log_request_body: false
|
||||
@@ -5,8 +5,14 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
import re
|
||||
from typing import Tuple
|
||||
from fastapi import FastAPI
|
||||
import time
|
||||
import json
|
||||
import hashlib
|
||||
import hmac
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import yaml
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -15,6 +21,103 @@ REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
|
||||
app = FastAPI(title="卡若AI 网关", version="1.0")
|
||||
|
||||
DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent / "config" / "gateway.yaml"
|
||||
|
||||
|
||||
def _is_abs_path(p: str) -> bool:
|
||||
try:
|
||||
return Path(p).is_absolute()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _resolve_path(p: str) -> Path:
|
||||
if _is_abs_path(p):
|
||||
return Path(p)
|
||||
return REPO_ROOT / p
|
||||
|
||||
|
||||
def _read_yaml(path: Path) -> Dict[str, Any]:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
|
||||
|
||||
def load_config() -> Dict[str, Any]:
|
||||
"""
|
||||
读取网关配置(多租户/鉴权/白名单等)。
|
||||
- 默认路径:config/gateway.yaml(不入库;你可以从 gateway.example.yaml 复制一份)
|
||||
- 也支持通过环境变量 KARUO_GATEWAY_CONFIG 指定绝对/相对路径
|
||||
"""
|
||||
p = os.environ.get("KARUO_GATEWAY_CONFIG", "").strip()
|
||||
cfg_path = _resolve_path(p) if p else DEFAULT_CONFIG_PATH
|
||||
if not cfg_path.exists():
|
||||
return {}
|
||||
try:
|
||||
return _read_yaml(cfg_path)
|
||||
except Exception:
|
||||
# 配置读失败时不要“悄悄放行”,避免外网误用
|
||||
raise
|
||||
|
||||
|
||||
def _sha256_hex(s: str) -> str:
|
||||
return hashlib.sha256(s.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _get_salt(cfg: Dict[str, Any]) -> str:
|
||||
auth = (cfg or {}).get("auth") or {}
|
||||
salt_env = auth.get("salt_env", "KARUO_GATEWAY_SALT")
|
||||
return os.environ.get(salt_env, "")
|
||||
|
||||
|
||||
def _auth_header_name(cfg: Dict[str, Any]) -> str:
|
||||
auth = (cfg or {}).get("auth") or {}
|
||||
return auth.get("header_name", "X-Karuo-Api-Key")
|
||||
|
||||
|
||||
def _tenant_by_key(cfg: Dict[str, Any], api_key_plain: str) -> Optional[Dict[str, Any]]:
|
||||
tenants = (cfg or {}).get("tenants") or []
|
||||
if not api_key_plain:
|
||||
return None
|
||||
salt = _get_salt(cfg)
|
||||
if not salt:
|
||||
return None
|
||||
key_hash = _sha256_hex(api_key_plain + salt)
|
||||
for t in tenants:
|
||||
if not isinstance(t, dict):
|
||||
continue
|
||||
stored = str(t.get("api_key_sha256", "")).strip()
|
||||
if stored and hmac.compare_digest(stored, key_hash):
|
||||
return t
|
||||
return None
|
||||
|
||||
|
||||
def _rpm_allow(tenant_id: str, rpm: int) -> bool:
|
||||
"""
|
||||
极简内存限流(单进程);够用就行。
|
||||
- 生产建议用 Nginx/网关层限流
|
||||
"""
|
||||
if rpm <= 0:
|
||||
return True
|
||||
now = time.time()
|
||||
window = int(now // 60)
|
||||
key = f"{tenant_id}:{window}"
|
||||
bucket = app.state.__dict__.setdefault("_rpm_bucket", {})
|
||||
cnt = bucket.get(key, 0) + 1
|
||||
bucket[key] = cnt
|
||||
return cnt <= rpm
|
||||
|
||||
|
||||
def _log_access(cfg: Dict[str, Any], record: Dict[str, Any]) -> None:
|
||||
logging_cfg = (cfg or {}).get("logging") or {}
|
||||
if not logging_cfg.get("enabled", False):
|
||||
return
|
||||
path_raw = str(logging_cfg.get("path", "")).strip()
|
||||
if not path_raw:
|
||||
return
|
||||
p = _resolve_path(path_raw)
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
with p.open("a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
||||
|
||||
|
||||
def load_bootstrap() -> str:
|
||||
p = REPO_ROOT / "BOOTSTRAP.md"
|
||||
@@ -24,14 +127,31 @@ def load_bootstrap() -> str:
|
||||
|
||||
|
||||
def load_registry() -> str:
|
||||
p = REPO_ROOT / "SKILL_REGISTRY.md"
|
||||
cfg = load_config()
|
||||
skills_cfg = (cfg or {}).get("skills") or {}
|
||||
reg_path = skills_cfg.get("registry_path", "SKILL_REGISTRY.md")
|
||||
p = _resolve_path(reg_path)
|
||||
if p.exists():
|
||||
return p.read_text(encoding="utf-8")
|
||||
return "技能注册表未找到。"
|
||||
|
||||
|
||||
def match_skill(prompt: str) -> Tuple[str, str]:
|
||||
"""根据 prompt 在 SKILL_REGISTRY 中匹配技能,返回 (技能名, 路径)。"""
|
||||
def _normalize_trigger_token(t: str) -> str:
|
||||
t = t.strip()
|
||||
t = t.replace("**", "").replace("*", "")
|
||||
t = t.replace("`", "")
|
||||
return t.strip()
|
||||
|
||||
|
||||
def _split_triggers(triggers: str) -> List[str]:
|
||||
s = triggers or ""
|
||||
for ch in ["、", ",", ",", ";", ";", "|", "/"]:
|
||||
s = s.replace(ch, " ")
|
||||
return [tok for tok in (_normalize_trigger_token(x) for x in s.split()) if tok]
|
||||
|
||||
|
||||
def match_skill(prompt: str, cfg: Optional[Dict[str, Any]] = None) -> Tuple[str, str, str]:
|
||||
"""根据 prompt 在 SKILL_REGISTRY 中匹配技能,返回 (技能ID, 技能名, 路径)。"""
|
||||
text = load_registry()
|
||||
lines = text.split("\n")
|
||||
for line in lines:
|
||||
@@ -39,13 +159,21 @@ def match_skill(prompt: str) -> Tuple[str, str]:
|
||||
continue
|
||||
parts = [p.strip() for p in line.split("|")]
|
||||
# 表列:| # | 技能 | 成员 | 触发词 | SKILL 路径 | 一句话 |
|
||||
if len(parts) < 6:
|
||||
if len(parts) < 7:
|
||||
continue
|
||||
skill_name, triggers, path = parts[2], parts[4], parts[5].strip("`")
|
||||
for t in triggers.replace("、", " ").split():
|
||||
skill_id = parts[1]
|
||||
skill_name = parts[2]
|
||||
triggers = parts[4]
|
||||
path = parts[5].strip("`")
|
||||
for t in _split_triggers(triggers):
|
||||
if t and t in prompt:
|
||||
return skill_name, path
|
||||
return "通用", "总索引.md"
|
||||
return skill_id, skill_name, path
|
||||
|
||||
skills_cfg = (cfg or load_config() or {}).get("skills") or {}
|
||||
on_no_match = skills_cfg.get("on_no_match", "allow_general")
|
||||
if on_no_match == "deny":
|
||||
return "", "", ""
|
||||
return "GENERAL", "通用", "总索引.md"
|
||||
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
@@ -54,11 +182,18 @@ class ChatRequest(BaseModel):
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
reply: str
|
||||
tenant_id: str = ""
|
||||
tenant_name: str = ""
|
||||
skill_id: str = ""
|
||||
matched_skill: str
|
||||
skill_path: str
|
||||
|
||||
|
||||
def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> str:
|
||||
def _llm_settings(cfg: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return (cfg or {}).get("llm") or {}
|
||||
|
||||
|
||||
def build_reply_with_llm(prompt: str, cfg: Dict[str, Any], matched_skill: str, skill_path: str) -> str:
|
||||
"""调用 LLM 生成回复(OpenAI 兼容)。未配置则返回模板回复。"""
|
||||
bootstrap = load_bootstrap()
|
||||
system = (
|
||||
@@ -66,8 +201,9 @@ def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> st
|
||||
f"当前匹配技能:{matched_skill},路径:{skill_path}。"
|
||||
"先简短思考并输出,再给执行要点,最后必须带「[卡若复盘]」块(含目标·结果·达成率、过程 1 2 3、反思、总结、下一步)。"
|
||||
)
|
||||
api_key = os.environ.get("OPENAI_API_KEY")
|
||||
base_url = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1")
|
||||
llm_cfg = _llm_settings(cfg)
|
||||
api_key = os.environ.get(llm_cfg.get("api_key_env", "OPENAI_API_KEY"))
|
||||
base_url = os.environ.get(llm_cfg.get("api_base_env", "OPENAI_API_BASE"), "https://api.openai.com/v1")
|
||||
if api_key:
|
||||
try:
|
||||
import httpx
|
||||
@@ -75,11 +211,11 @@ def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> st
|
||||
f"{base_url.rstrip('/')}/chat/completions",
|
||||
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
|
||||
json={
|
||||
"model": os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
|
||||
"model": os.environ.get(llm_cfg.get("model_env", "OPENAI_MODEL"), "gpt-4o-mini"),
|
||||
"messages": [{"role": "system", "content": system}, {"role": "user", "content": prompt}],
|
||||
"max_tokens": 2000,
|
||||
"max_tokens": int(llm_cfg.get("max_tokens", 2000)),
|
||||
},
|
||||
timeout=60.0,
|
||||
timeout=float(llm_cfg.get("timeout_seconds", 60)),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
@@ -131,11 +267,101 @@ def index():
|
||||
|
||||
|
||||
@app.post("/v1/chat", response_model=ChatResponse)
|
||||
def chat(req: ChatRequest):
|
||||
async def chat(req: ChatRequest, request: Request):
|
||||
"""外部调用入口:传入 prompt,返回按卡若AI 流程生成的 reply。"""
|
||||
matched_skill, skill_path = match_skill(req.prompt)
|
||||
reply = build_reply_with_llm(req.prompt, matched_skill, skill_path)
|
||||
return ChatResponse(reply=reply, matched_skill=matched_skill, skill_path=skill_path)
|
||||
cfg = load_config()
|
||||
|
||||
# 1) 鉴权(如果有配置文件就强制开启)
|
||||
tenant: Optional[Dict[str, Any]] = None
|
||||
if cfg:
|
||||
header_name = _auth_header_name(cfg)
|
||||
api_key = request.headers.get(header_name, "")
|
||||
tenant = _tenant_by_key(cfg, api_key)
|
||||
if not tenant:
|
||||
raise HTTPException(status_code=401, detail="invalid api key")
|
||||
|
||||
tenant_id = str((tenant or {}).get("id", "")).strip()
|
||||
tenant_name = str((tenant or {}).get("name", "")).strip()
|
||||
|
||||
# 2) 限流/输入限制(可选)
|
||||
limits = (tenant or {}).get("limits") or {}
|
||||
max_prompt_chars = int(limits.get("max_prompt_chars", 0) or 0)
|
||||
if max_prompt_chars and len(req.prompt) > max_prompt_chars:
|
||||
raise HTTPException(status_code=413, detail="prompt too large")
|
||||
|
||||
rpm = int(limits.get("rpm", 0) or 0)
|
||||
if tenant_id and rpm and not _rpm_allow(tenant_id, rpm):
|
||||
raise HTTPException(status_code=429, detail="rate limit exceeded")
|
||||
|
||||
# 3) 技能匹配 + 白名单校验
|
||||
skill_id, matched_skill, skill_path = match_skill(req.prompt, cfg=cfg)
|
||||
if cfg and not (skill_id and matched_skill and skill_path):
|
||||
raise HTTPException(status_code=404, detail="no skill matched")
|
||||
|
||||
if tenant:
|
||||
allowed = (tenant.get("allowed_skills") or []) if isinstance(tenant, dict) else []
|
||||
allowed = [str(x).strip() for x in allowed if str(x).strip()]
|
||||
if allowed:
|
||||
# 同时支持“技能ID白名单”和“SKILL路径白名单”
|
||||
if (skill_id not in allowed) and (skill_path not in allowed):
|
||||
raise HTTPException(status_code=403, detail="skill not allowed for tenant")
|
||||
|
||||
reply = build_reply_with_llm(req.prompt, cfg, matched_skill, skill_path)
|
||||
|
||||
# 4) 访问日志(默认不落 prompt 内容)
|
||||
logging_cfg = (cfg or {}).get("logging") or {}
|
||||
record: Dict[str, Any] = {
|
||||
"ts": int(time.time()),
|
||||
"tenant_id": tenant_id,
|
||||
"tenant_name": tenant_name,
|
||||
"skill_id": skill_id,
|
||||
"matched_skill": matched_skill,
|
||||
"skill_path": skill_path,
|
||||
"client": request.client.host if request.client else "",
|
||||
"ua": request.headers.get("user-agent", ""),
|
||||
}
|
||||
if bool(logging_cfg.get("log_request_body", False)):
|
||||
record["prompt"] = req.prompt
|
||||
_log_access(cfg, record)
|
||||
|
||||
return ChatResponse(
|
||||
reply=reply,
|
||||
tenant_id=tenant_id,
|
||||
tenant_name=tenant_name,
|
||||
skill_id=skill_id,
|
||||
matched_skill=matched_skill,
|
||||
skill_path=skill_path,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/v1/health")
|
||||
def health():
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.get("/v1/skills")
|
||||
def allowed_skills(request: Request):
|
||||
"""
|
||||
返回该 tenant 允许的技能清单(需要 key)。
|
||||
用途:部门侧自查权限/联调。
|
||||
"""
|
||||
cfg = load_config()
|
||||
if not cfg:
|
||||
return {"tenants_enabled": False, "allowed_skills": []}
|
||||
header_name = _auth_header_name(cfg)
|
||||
api_key = request.headers.get(header_name, "")
|
||||
tenant = _tenant_by_key(cfg, api_key)
|
||||
if not tenant:
|
||||
raise HTTPException(status_code=401, detail="invalid api key")
|
||||
allowed = tenant.get("allowed_skills") or []
|
||||
allowed = [str(x).strip() for x in allowed if str(x).strip()]
|
||||
return {
|
||||
"tenants_enabled": True,
|
||||
"tenant_id": str(tenant.get("id", "")).strip(),
|
||||
"tenant_name": str(tenant.get("name", "")).strip(),
|
||||
"allowed_skills": allowed,
|
||||
"header_name": header_name,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,3 +2,4 @@ fastapi>=0.100.0
|
||||
uvicorn>=0.22.0
|
||||
httpx>=0.24.0
|
||||
pydantic>=2.0
|
||||
PyYAML>=6.0
|
||||
|
||||
64
运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py
Normal file
64
运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成“部门/科室”的 API Key(明文只输出一次)并给出写入 gateway.yaml 的 sha256。
|
||||
|
||||
用法:
|
||||
export KARUO_GATEWAY_SALT="your-long-random-salt"
|
||||
python tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科"
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import secrets
|
||||
import sys
|
||||
|
||||
|
||||
def sha256_hex(s: str) -> str:
|
||||
return hashlib.sha256(s.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tenant-id", required=True, help="tenant id, e.g. finance")
|
||||
parser.add_argument("--tenant-name", default="", help='tenant name, e.g. "财务科"')
|
||||
parser.add_argument(
|
||||
"--key",
|
||||
default="",
|
||||
help="可选:自定义明文 key(不建议)。为空则自动生成。",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--salt-env",
|
||||
default="KARUO_GATEWAY_SALT",
|
||||
help="salt 的环境变量名,默认 KARUO_GATEWAY_SALT",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
salt = os.environ.get(args.salt_env, "")
|
||||
if not salt:
|
||||
print(f"[ERROR] 未读取到 salt 环境变量:{args.salt_env}", file=sys.stderr)
|
||||
print("建议:export KARUO_GATEWAY_SALT=\"一个足够长的随机字符串\"", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
dept_key = args.key or secrets.token_urlsafe(32)
|
||||
key_hash = sha256_hex(dept_key + salt)
|
||||
|
||||
# 明文 key 只输出一次;请在生成后立即给到部门系统的安全配置里。
|
||||
print("tenant:")
|
||||
print(f" id: {args.tenant_id}")
|
||||
if args.tenant_name:
|
||||
print(f" name: {args.tenant_name}")
|
||||
print("")
|
||||
print("dept_key (plaintext, show once):")
|
||||
print(dept_key)
|
||||
print("")
|
||||
print("api_key_sha256 (write into gateway.yaml):")
|
||||
print(key_hash)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -82,6 +82,42 @@ uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
|
||||
---
|
||||
|
||||
## 四点五、接口配置化(科室/部门可复制)
|
||||
|
||||
> 目标:让以后任何科室/部门/合作方都能“拿到一套配置 + 一个 key”,直接调用卡若AI 网关,不需要改代码。
|
||||
|
||||
### 你需要提前准备什么(一次性)
|
||||
|
||||
1. **一个 salt**(只放环境变量,不写入仓库):`KARUO_GATEWAY_SALT`
|
||||
2. (可选)如果要真实 LLM 输出:`OPENAI_API_KEY`(以及 `OPENAI_API_BASE`、`OPENAI_MODEL`)
|
||||
3. 外网场景:域名/反代已就绪(宝塔/Nginx)或 ngrok 临时暴露
|
||||
|
||||
### 配置文件在哪里
|
||||
|
||||
- 示例:`运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml`
|
||||
- 实际:`运营中枢/scripts/karuo_ai_gateway/config/gateway.yaml`(建议不提交到仓库)
|
||||
- 也可用环境变量指定:`KARUO_GATEWAY_CONFIG=/path/to/gateway.yaml`
|
||||
|
||||
### 新增一个科室/部门(标准步骤)
|
||||
|
||||
1. 设置 salt(运行环境):
|
||||
- `export KARUO_GATEWAY_SALT="一个足够长的随机字符串"`
|
||||
2. 生成部门 key(明文只输出一次)与 hash:
|
||||
- `python 运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科"`
|
||||
3. 将输出的 `api_key_sha256` 写入 `config/gateway.yaml` 的对应 tenant
|
||||
4. 配置该 tenant 的 `allowed_skills`(技能白名单:支持技能ID如 `E05a`,或 SKILL 路径)
|
||||
5. 重启网关服务
|
||||
|
||||
### 调用方式(必须带部门 key)
|
||||
|
||||
- `POST /v1/chat`:
|
||||
- Header:`X-Karuo-Api-Key: <dept_key>`
|
||||
- Body:`{"prompt":"你的问题"}`
|
||||
- `GET /v1/skills`:部门自查当前允许技能(同样需要 key)
|
||||
- `GET /v1/health`:健康检查(无需 key)
|
||||
|
||||
---
|
||||
|
||||
## 五、最终:执行命令与链接(给 Cursor / 其他 AI 用)
|
||||
|
||||
**固定域名**:`https://kr-ai.quwanzhi.com`(部署与配置见「内网穿透与域名配置_卡若AI标准方案.md」)。
|
||||
|
||||
@@ -125,3 +125,4 @@
|
||||
| 2026-02-24 05:49:20 | 🔄 卡若AI 同步 2026-02-24 05:49 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 10 个 |
|
||||
| 2026-02-24 11:42:10 | 🔄 卡若AI 同步 2026-02-24 11:42 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 10 个 |
|
||||
| 2026-02-24 16:28:06 | 🔄 卡若AI 同步 2026-02-24 16:28 | 更新:水桥平台对接、卡木、卡土、运营中枢工作台 | 排除 >20MB: 10 个 |
|
||||
| 2026-02-24 16:49:15 | 🔄 卡若AI 同步 2026-02-24 16:49 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 |
|
||||
|
||||
@@ -128,3 +128,4 @@
|
||||
| 2026-02-24 05:49:20 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 05:49 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-24 11:42:10 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 11:42 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-24 16:28:06 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 16:28 | 更新:水桥平台对接、卡木、卡土、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-24 16:49:15 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 16:49 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
|
||||
Reference in New Issue
Block a user