本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/docs/社区银行插件开发方案_2026-09-30.md。
社区银行插件 · 开发计划方案
日期:2026-09-30 | 依据:
docs/插件开发规范.md(1901 行,v2026-09-30 13:52)状态:待拍板 · 未动工 | 提议插件名:
社区银行/ 目录plugins/community_bank// 命名空间Plugin\CommunityBank
0. 结论速览
- 技术上完全可行,且站点已具备全部前置条件:积分增减出口完备(
Points::award/deduct)、插件独立库与事务机制成熟(points_mall/payment_center两套现成样板)。 - 但有一个硬约束必须先接受:本站积分经济极其集中且正在通缩(详见 §1.2)——
285 人共 23422 分,站长一人占 38%;近 7 天净增 −1582。
→ 银行若按常规利率发息,等于给最富的人印钞。利率必须极低 + 分段递减,且银行整体定位应是「沉淀池 + 玩法」而非「产出源」。
- 第二个硬约束:核心
cron_trigger.php没有任何插件钩子,插件也不能往核心队列塞自定义任务类型(规范 §11.4 #26,会被静默丢弃)。
→ 利息结算不做定时任务,改用惰性结算(lazy accrual):用户访问银行时按「距上次结算的秒数」补算。这也符合本站「285 用户」的体量。
- 建议范围:P0–P2 先做「单银行 + 活期 + 分段利率 + 流水 + 存款排行」这一最小闭环;
定期存款、转账、贷款放 P3/P4,贷款默认不做(它是唯一会凭空造币的功能,风险最高)。
- 6 个决策点需要你拍板(§3),其中决策点 2(利率模型)和决策点 4(贷款)最关键。
1. 调研依据(全部实测到行号)
1.1 积分体系:出口完备,但有三个坑
| 能力 | 位置 | 说明 |
|---|---|---|
| 加积分 | app/Helpers/Points.php:161 award($uid,$points,$reason,$relatedId,$relatedType) | 内部含升级判定 + 通知 + points_award_after 钩子 |
| 扣积分 | app/Helpers/Points.php:211 deduct(...) → bool | 返回 false = 余额不足 |
| 原子防透支 | app/Helpers/Points.php:224-231 | UPDATE ... WHERE points >= :p + rowCount()===0 判定 |
| 嵌套事务感知 | app/Helpers/Points.php:218 | 外层有事务时不自建、不自行 commit |
⚠️ 坑 1(规范 §11.23):核心从不往 Session 写积分 —— 没有 $_SESSION['user_points']。
(int)($_SESSION['user_points'] ?? 0) 恒为 0 且不报错。正确写法:
$me = \app\Helpers\Auth::getCurrentUser(); // 已含 points 字段
$myPoints = (int)($me['points'] ?? 0);⚠️ 坑 2:Auth::getCurrentUser() 逐请求静态缓存(Auth.php:31)→
同请求内刚 Points::award() 完再调它拿不到新值(须 Auth::clearUserCache(),Auth.php:107)。
→ 银行页面「存完钱立刻刷新余额」必须走插件自己的库读余额,不要读 session 里的 users.points。
⚠️ 坑 3:deduct() 返回 false 时没有异常、没有日志 —— 银行侧必须自己把「余额不足」翻译成用户可读提示。
1.2 ★ 经济现状实测(本方案最硬的约束)
用户数=285 积分总额=23422 最高=8940 均值=82.2
积分分布:0 分 → 7 人 | 1–99 分 → 273 人 | 100–999 分 → 4 人 | 1k–9999 分 → 1 人
TOP5:flinthub(id=1) 8940 | Evan 261 | 我自逍遥 207 | ahfanxin 172 | wuliannan6066 115
近 7 天 points_log:71 条,净增 −1582,日均 −226(通缩)
全量最大产出源:AI 自动回复 +2962(1481 条)
其他来源:抢红包 +516 | 发布主题 +455 | 回帖 +330 | 摇骰子一等奖 +260
主要消耗:道具礼包 −1000 | 发红包 −760 | 飞刀头像商店 −600 | 摇骰子投注 −180三条推论(直接决定设计):
- 利率必须极低。全站总额才 23422 分 —— 即便全站存款按日息 0.05% 计,日产出也只有 11.7 分,
仅为当前日通缩量(−226)的 5%。这是安全区。反之,日息若设 1%,日产出 234 分,瞬间把通缩扭成通胀。
- 必须分段递减。站长一人 8940 分,若与他同利率,等于给他开专属印钞机。
→ 这正是 DZ 社区银行的「分段活期利率」设计(§2),本站要抄的就是这条。
- 银行要做「沉淀池」。用开户费 / 转账手续费 / 定期提前支取违约金这些 sink,
对冲利息这个 source,让银行整体净中性甚至净消耗。
1.3 ★ 调度能力:没有插件 cron 钩子(关键技术约束)
实测 cron_trigger.php(274 行,腾讯云函数每 5 分钟触发):
- 任务全部硬编码:
session_gc/audit_log_purge/rate_cache_gc/runtime_calibrate/
ai_flush / 3 个队列消费 / archive_mark / orphan_attachments。
- 全文没有任何
Plugin::hook(...)调用 → 插件拿不到定时执行权。 - 唯一一处插件调用是硬编码的:
cron_trigger.php:183\Plugin\AiAssistant\Plugin::flushBatch(20)。 - 其节流靠局部闭包
$onceEvery($key,$sec)(:101,写data/runtime/cron_last_*.ts)——
闭包作用域,插件调不到。
规范给出的两条出路:
| 出处 | 内容 |
|---|---|
| §11.4 #26 | 插件不能往核心队列塞自定义类型(消费者只认 rebuild_search/stats,未知类型 error_log 后直接标记 done = 任务丢失)。可用 Queue::push($type,$data,$idx,$dataPath) 推插件自有队列 + 自带消费端。 |
| §11.20 | 收尾/异步活儿挂 route_after_dispatch(index.php:321,单触发、响应已产出);⚠️ 每页都触发 → 必须节流。先例 plugins/user_level/hook/route_after_dispatch.php。 |
先例读后(user_level/hook/route_after_dispatch.php,52 行):
if (!\app\Helpers\Auth::isLoggedIn()) return; // ① 只对登录用户
$last = (int)($_SESSION['ul_last_sync'] ?? 0);
if ($now - $last >= 600) { // ② 600s 节流(每用户)
$_SESSION['ul_last_sync'] = $now;
if (\Plugin\user_level\Plugin::budget(3)) { ... } // ③ 查询预算闸门
}→ 本方案采纳:利息结算走 route_after_dispatch + $_SESSION 节流(每人每 N 分钟最多一次),
不做定时任务、不碰核心队列。
1.4 独立库与事务
- 独立库:
\app\Helpers\Plugin::db('community_bank')→ 原生 PDO。
⚠️ 没有 $db->query($sql, $params) 便捷绑定,必须自己 prepare()/execute()(规范 §10.1)。
- 事务:
beginTransaction()/commit()/rollBack()。
⚠️ $db->begin() 不存在(PDO 无此方法);catch 内必须先 inTransaction() 再回滚,
否则二次抛异常逃逸 → HTML 错误页 → AJAX r.json() 失败(规范 §10.2,medal 插件踩过)。
- DDL:必须 SQLite 语法(
INTEGER PRIMARY KEY/TEXT/CREATE TABLE IF NOT EXISTS,规范 §10.3)。 - ⚠️ 本方案特有风险:跨库无事务。插件库
community_bank.sqlite与核心库
business.sqlite(users.points)是两个独立 SQLite 文件 → 无法同事务。
「扣银行余额」与「加用户积分」之间没有原子性保证 → 必须自己做补偿 + 对账(详见 §7.3)。
1.5 后台 Tab:复用核心 .admin-tabs,勿自造(规范 §11.10)
核心已有现成组件:assets/css/admin.css:1041-1067
.admin-tabs { display:flex; flex-wrap:wrap; gap:4px; border-bottom:2px solid var(--c-border); margin-bottom:18px; }
.admin-tab-btn { padding:8px 18px; font-size:13px; color:var(--c-text-muted); ... }
.admin-tab-btn.active { color:var(--c-primary); font-weight:600; border-bottom-color:var(--c-primary); }
.admin-tab-pane { display:none; } .admin-tab-pane.active { display:block; }约定(§11.10):纯 JS 切换(读 data-tab,零内联 onclick);保存后必须留在当前 tab;每个面板表单各自带 csrf。
📌 顺带纠正一处历史欠账:
plugins/payment_center的.pc-tabs(2026-09-30 自建)属于重复造轮子 ——当时误判为「核心无全局 Tab 组件」。实际上核心有
.admin-tabs。两者用途略有差别(
.admin-tabs是同页内 JS 切面板;pc-tabs是跨页链接导航),但视觉上完全可复用
.admin-tabs+ 把<button>换成<a>。建议:本次银行插件直接用核心组件;
payment_center的pc-tabs择机回收(低优先,不影响功能)。
1.6 可复用范式
| 范式 | 路径 | 抄什么 |
|---|---|---|
| 积分经济类 | plugins/points_mall/ | 目录结构(7 个 hook 文件)、表前缀 pm_、views/{shop,item,admin}.php 三段式 |
| 资金/订单类 | plugins/payment_center/ | 订单状态机、幂等去重、审计日志、后台二次验证门禁 |
| 定时收尾类 | plugins/user_level/hook/route_after_dispatch.php | $_SESSION 节流 + budget() 闸门 + 全 try/catch |
points_mall 目录(照抄即可):
plugins/points_mall/
├── AdminController.php FrontController.php Plugin.php
├── assets/{script.js,style.css}
├── data/{points_mall.sqlite,.htaccess,web.config}
├── hook/{admin_route_register,controller_view_before,init_after,layout_head_end,
│ nav_plugin_links,profile_user_card_after,route_register}.php
├── lang/{zh,zh_tw,en}.php
├── views/{admin,item,shop}.php
├── plugin.json 说明.md1.7 命名与路由
- 目录
plugins/community_bank/;命名空间Plugin\CommunityBank
(规范 §11.34:命名空间首段经 camelToSnake 必须逐字节等于目录名 → CommunityBank → community_bank ✅)。
★ 交付前必跑 .workbuddy-ai/tools/plugin_case_lint.py(本机 Windows 大小写不敏感 = 假通过;Linux 直接 500)。
- 前台路由注册(模板见
points_mall/hook/route_register.php):
if (!isset($router)) return;
require_once __DIR__ . '/../FrontController.php';
$router->get('/bank', ['\Plugin\CommunityBank\FrontController', 'index']);- ⚠️ 后台路由:
admin_route_register里不要再写/admin前缀(规范 §11.22,否则 404)。
2. 竞品功能盘点
2.1 Discuz! 社区银行(V4.0,作者 LFLY1573)
- 多银行模式 —— 会员也可自建银行
- 银行融资、活期、定期、贷款、汇款
- ★ 首创分段活期利率 —— 按金额大小改变活期利率
- 完善的日志记录,方便查实每项操作
- 独立的安装 / 反安装文件
2.2 其他程序的社区银行
| 来源 | 特色功能 |
|---|---|
| MineBBS · LSE 银行插件 | 存款利息、信用贷款、每日结算、严厉的逾期惩罚 |
| MineBBS · 玩家银行 + 股票 | 玩家自建银行、贷款、定期、银行卡、股票、信用分、逾期风控、银行冻结、打工还债 |
[bbs1.org [bank]](https://bbs1.org/topic/897) | 活期 + 多档定期、利息记录、账户管理、存款排行榜 |
| GitHub · znc15/Bank | 活期、定期、贷款 + PlaceholderAPI 变量 |
2.3 功能矩阵(★ = 本站建议做)
| 功能 | DZ | 其他 | 本站建议 | 理由 |
|---|---|---|---|---|
| 开户 / 销户 | ✅ | ✅ | ★ 做 | 基础;开户费可作 sink |
| 活期存取 | ✅ | ✅ | ★ 做 | 核心 |
| 分段活期利率 | ✅ | — | ★ 必做 | §1.2 推论 2:治「富人越富」 |
| 定期存款(多档期限) | ✅ | ✅ | ◐ P3 | 有玩法价值,但需到期调度 |
| 转账 / 汇款 | ✅ | ✅ | ◐ P3 | 手续费可作 sink;需防洗分 |
| 存款排行榜 | — | ✅ | ★ 做 | 成本极低,与既有 leaderboard 插件同思路 |
| 流水 / 利息记录 | ✅ | ✅ | ★ 做 | 资金类插件必备(对齐 payment_center 审计) |
| 利息结算 | ✅ | ✅ | ★ 做(惰性) | §1.3:不能做定时任务 |
| 贷款 | ✅ | ✅ | ✗ 默认不做 | 唯一会凭空造币的功能,风险最高(§3 决策点 4) |
| 信用分 / 逾期风控 / 银行冻结 | — | ✅ | ✗ 不做 | 依赖贷款,无贷款则无意义 |
| 多银行 / 会员自建银行 | ✅ | ✅ | ✗ 不做 | 与「极简」取向冲突,且管理成本高 |
| 股票 / 银行卡 / 打工还债 | — | ✅ | ✗ 不做 | 超出「社区银行」范畴 |
3. ⚠️ 决策点(需要你拍板)
决策点 1:单银行 还是 多银行?
- A(推荐)单银行:全站一个「社区银行」,利率由后台统一配置。实现最简,符合极简取向。
- B 多银行:后台可建多家银行,各自利率不同,用户可选。DZ 的卖点,但后台要多一套 CRUD,前台多一层选择。
- C 会员自建银行:DZ 有,但强烈不建议 —— 等于让用户自己定利率,必然被刷。
决策点 2:★ 利率模型(最关键)
- A(推荐)固定日息 + 分段递减:如
| 存款区间 | 日利率 |
|---|---|
| 0 – 999 | 0.08% |
| 1000 – 4999 | 0.03% |
| ≥ 5000 | 0.01% |
理由:直接治「站长 8940 分独占 38%」的问题;全站日产出上界 ≈ 23422×0.08% ≈ 18.7 分/天,远低于当前日通缩 226。
- B 固定单一利率:最简单,但富人线性受益,长期加剧集中。
- C 动态利率(挂钩全站积分总量):利率 = f(总额),自动抑制通胀。最"聪明"但最复杂、用户不可预期。
- D 零利率 + 纯玩法:银行只做「保管 + 排行 + 转账」,完全不发息。最安全,但少了银行的核心乐趣。
我的建议:A。若你想要「零通胀风险」,选 D(但那样银行的存在感会弱很多)。
决策点 3:定期存款做不做?
- A 不做(P0–P2 只做活期):最省。
- B 做(P3):期限 7 / 30 / 90 天,利率高于活期(如日息 0.10% / 0.13% / 0.16%)。
需要处理:到期自动转活期、提前支取按活期计息 + 违约金。
决策点 4:★ 贷款做不做?(风险最高)
贷款是唯一会凭空创造积分的功能(不是从别人余额里挪)。本站总额才 23422 分,
一笔 5000 分贷款 = 全站增发 21%。我的建议:默认不做。
若一定要做,最低风险形态是:
- 只允许定期存单质押贷款(借出 ≤ 存单本金的 80%)→ 本质是抵押借贷,不增发
- 额度硬上限(如单笔 ≤ 1000 分)
- 逾期自动扣划存单 + 冻结账户
决策点 5:转账 / 汇款做不做?
- A 做(P3):用户间转积分,收手续费(如 1%,最低 1 分)→ 手续费是天然 sink,对冲利息。
- B 不做:少一个洗分/刷分风险点(虽有小号互转,但手续费能抑制)。
决策点 6:利息结算方式(技术向,已基本定调)
- A(推荐)惰性结算:
route_after_dispatch+$_SESSION节流(每人每 10 分钟最多算一次),
按 now - last_accrual_at 补息。无定时任务、无核心改动。
- B 定时任务:需要改核心
cron_trigger.php加钩子 —— 违背插件哲学,不建议。 - C 插件自有队列 + 自带消费端(§11.4 #26):可行但过重,285 用户量级没必要。
4. 数据模型
库:plugins/community_bank/data/community_bank.sqlite(前缀 cb_)
4.1 cb_accounts —— 银行账户
CREATE TABLE IF NOT EXISTS cb_accounts (
user_id INTEGER PRIMARY KEY, -- 一人一户
balance INTEGER NOT NULL DEFAULT 0, -- 活期余额(整数分/积分)
total_deposited INTEGER NOT NULL DEFAULT 0, -- 累计存入(用于排行 / 成就)
last_accrual_at INTEGER NOT NULL DEFAULT 0, -- ★ 上次结息时间戳(惰性结算依据)
opened_at INTEGER NOT NULL DEFAULT 0,
status INTEGER NOT NULL DEFAULT 1 -- 1 正常 / 0 冻结
);⚠️ 金额用 INTEGER(积分本就是整数),不要用浮点 —— 浮点累加会漂移。
4.2 cb_transactions —— 流水(对账依据)
CREATE TABLE IF NOT EXISTS cb_transactions (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
type TEXT NOT NULL, -- open/deposit/withdraw/interest/fixed_open/fixed_mature/transfer_in/transfer_out/fee
amount INTEGER NOT NULL, -- 正=入账,负=出账
balance_after INTEGER NOT NULL,
memo TEXT NOT NULL DEFAULT '',
related_id INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_cbt_user ON cb_transactions (user_id, id DESC);4.3 cb_rates —— 分段利率档位
CREATE TABLE IF NOT EXISTS cb_rates (
id INTEGER PRIMARY KEY,
min_amount INTEGER NOT NULL, -- 区间下界(含)
max_amount INTEGER NOT NULL, -- 区间上界(不含);0 或 -1 表示无穷
daily_bp INTEGER NOT NULL, -- 日利率,单位「万分之一」(basis point):8 = 0.08%
sort_order INTEGER NOT NULL DEFAULT 0
);用基点(bp)整数存利率,避免浮点;计算时
interest = floor(balance bp / 10000 days)。
4.4 cb_fixed_deposits —— 定期存单(若做)
CREATE TABLE IF NOT EXISTS cb_fixed_deposits (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
principal INTEGER NOT NULL,
daily_bp INTEGER NOT NULL,
term_days INTEGER NOT NULL,
start_at INTEGER NOT NULL,
mature_at INTEGER NOT NULL,
status INTEGER NOT NULL DEFAULT 0, -- 0 存续 / 1 已到期已转出 / 2 提前支取
accrued INTEGER NOT NULL DEFAULT 0 -- 已计提利息(惰性)
);4.5 cb_settings —— 全局设置(KV)
CREATE TABLE IF NOT EXISTS cb_settings (
k TEXT PRIMARY KEY,
v TEXT NOT NULL DEFAULT ''
);键例:open_fee / transfer_fee_bp / min_deposit / max_balance / accrual_throttle_sec / enabled。
5. 前台设计
5.1 路由(hook/route_register.php)
| 方法 | 路径 | 控制器方法 | 说明 |
|---|---|---|---|
| GET | /bank | index | 我的账户 + 存取款表单 + 最近流水 |
| POST | /bank/deposit | deposit | 存入(从 users.points → 银行余额) |
| POST | /bank/withdraw | withdraw | 取出(银行余额 → users.points) |
| GET | /bank/log | log | 完整流水(分页) |
| GET | /bank/rank | rank | 存款排行榜 |
| POST | /bank/open | open | 开户(收开户费) |
(P3 再加 /bank/transfer、/bank/fixed*)
5.2 入口
nav_plugin_links(左栏「应用」面板 + 顶部「应用」下拉)—— ⚠️ 双触发(规范 §9.3),
两个入口参数不同(__nav_uri 仅左栏那次有),必须按参数分别渲染;
⚠️ 该钩子文件禁声明顶层函数/类(双 include → Cannot redeclare → 第二处入口静默消失)。
- (可选)
right_sidebar_user_actions_above(#42):右栏用户栏内加个「银行」小按钮。
⚠️ 核心已提供 2 列网格容器 .mn-user-quick → 只输出裸条目,不要自带容器(§11.26)。
5.3 模板与样式
views/{index,log,rank}.php,三段式(section('title'/'css'/'content'))。- **CSS 只用既有
--mn-*变量(禁硬编码色值);.mn-section自身无 padding**,
直接当根容器必须自己补内边距(payment_center 踩过,见 REF §R.6 相邻条目)。
- 资产走
section('css')注入 +?v=filemtime自动破缓存。
6. 后台设计
6.1 路由(hook/admin_route_register.php,plugin.json 声明 permissions: ["route:admin"])
| 路径 | 说明 |
|---|---|
/admin/community-bank | 总览(存款总额 / 账户数 / 已发利息) |
/admin/community-bank/rates | 分段利率配置 |
/admin/community-bank/accounts | 账户列表(可冻结 / 手工调账) |
/admin/community-bank/log | 全站流水 |
/admin/community-bank/settings | 开户费 / 手续费 / 上限 |
⚠️ 不要写 /admin 前缀(§11.22)。
6.2 Tab 分区
复用核心 .admin-tabs / .admin-tab-btn / .admin-tab-pane(§11.10,勿自造)。
纯 JS 切换 + 保存后留在当前 tab + 每个面板独立 csrf。
6.3 二次验证门禁
资金类后台页必须挂二次验证(对齐 payment_center):校验 admin_verified_at + csrf。
⚠️ 继承 app\Core\Controller 不触发 Admin\BaseController 构造函数里的门禁 → 必须自补。
6.4 ⚠️ 设置保存的经典坑(§11.32)
(int)($_POST[$k] ?? 0) = 字段缺失静默写最小值 → 302 提示成功,但 DB 与 settings_cache.php 都不变、日志干净。
正解:array_key_exists($key, $_POST) 缺失即跳过;验证须同时回读 DB + 缓存文件。
7. 技术实现要点
7.1 钩子清单(拟 6 个)
| 钩子 | 文件 | 用途 |
|---|---|---|
init_after | hook/init_after.php | 惰性建表兜底 |
route_register | hook/route_register.php | 前台路由 |
admin_route_register | hook/admin_route_register.php | 后台路由 |
nav_plugin_links | hook/nav_plugin_links.php | 前台入口(⚠️ 双触发) |
layout_head_end | hook/layout_head_end.php | 按模板条件加载 CSS/JS |
route_after_dispatch | hook/route_after_dispatch.php | ★ 惰性利息结算(节流) |
7.2 ★ 惰性利息结算算法(本方案技术核心)
route_after_dispatch 触发(每页)
└─ 未登录 → return
└─ $_SESSION['cb_last_tick'] 距今 < 600s → return (每人 10 分钟一次)
└─ Plugin::budget(n) 闸门不足 → return (查询预算 ≤5)
└─ 开插件库:
SELECT balance, last_accrual_at FROM cb_accounts WHERE user_id = ?
days = floor((now - last_accrual_at) / 86400) ← 不足 1 天不计息
if days <= 0 → 只更新 last_accrual_at 不动余额 → return
bp = 按 balance 查 cb_rates 命中档位
interest = floor(balance * bp / 10000 * days)
UPDATE cb_accounts SET balance = balance + interest, last_accrual_at = ...
INSERT cb_transactions (type='interest', amount=+interest, ...)关键设计点:
- 按整日计息(
floor(秒/86400))—— 避免零头漂移;last_accrual_at按整日推进,不按now推进,
否则「每天访问 10 次」会吞掉不足 1 天的零头。
- 结算发生在插件库内部,不跨库 → 天然无 §1.4 的跨库事务问题。
- 不发
Points::award()—— 利息只记在银行余额里,只有取款时才动核心积分。
这大幅减少了跨库写次数,也让「积分总量」不被利息直接推高。
7.3 资金一致性(跨库无事务的兜底)
「存款」= 核心 Points::deduct() + 插件库 UPDATE cb_accounts,两个库、两个事务。
推荐顺序(先扣核心、后加银行):
1. Points::deduct(uid, amt, 'community_bank:deposit', ...) ← 返回 false 直接终止(余额不足)
2. 插件库 beginTransaction()
UPDATE cb_accounts SET balance = balance + amt ...
INSERT cb_transactions ...
commit()
3. 若第 2 步抛异常 → 立即 Points::award(uid, amt, 'community_bank:rollback') 补偿 + error_log取款则相反(先扣银行、后加核心),失败同理补偿。
对账:后台「总览」页显示 Σ cb_accounts.balance,并提供「与核心积分流水对账」按钮(按 cb_transactions 重算)。
7.4 查询预算
规范 §11.13 / §11.25:插件自己实现 budget(),单插件每请求所有 hook 查询总和 ≤ 5。
route_after_dispatch 里的结算必须走闸门;且先查 $_SESSION 节流标记,命中则零查询返回。
7.5 安全与卫生
- 所有写操作:先登录 → 后 CSRF(§11.28,
Auth::loginUrl()回跳)。 - 所有 AJAX:必须带 CSRF Token(红线 8);异常路径必须返回 JSON(§10.2)。
- 金额入参:
(int)强转 + 范围校验(防负数、防超大数)。 - 并发防透支:
UPDATE cb_accounts SET balance = balance - :a WHERE user_id = :u AND balance >= :a+rowCount()判定(对齐Points::deduct)。 - 三语文案:
lang/{zh,zh_tw,en}.php,键集合必须零差异(基线:三语各 1343 键)。
8. 分阶段实施计划
每阶段结束停下来给你验收(沿用既有节奏)。
| 阶段 | 内容 | 可独立验证的产物 |
|---|---|---|
| P0 | 骨架:plugin.json / Plugin.php / 独立库建表 / 6 个 hook 文件 / 三语空包 | 插件能在后台激活;init_after 建表成功;无 500 |
| P1 | 后台:.admin-tabs 四分区(利率 / 账户 / 流水 / 设置)+ 二次验证 | 后台可配分段利率;设置保存后回读 DB + 缓存一致 |
| P2 | 前台:开户 / 存取款 / 流水 / 排行 + nav_plugin_links 入口 | HTTP 冒烟:开户→存款→取款→流水闭环;余额与核心积分双向对账为 0 差 |
| P3 | 惰性利息结算 + 定期存款(若决策点 3 选 B)+ 转账(若决策点 5 选 A) | 时间旅行探针:伪造 last_accrual_at 为 N 天前 → 断言补息金额正确;跨天边界 / 0 天 / 负天数 |
| P4 | 贷款(仅当决策点 4 选「做」)+ 对账工具 + 说明.md | 逾期扣划探针;全库对账 |
附注:payment_center 的历史欠账(低优先)
.pc-tabs 可择机回收为 .admin-tabs(§1.5)。不影响功能,不建议本次一并动,避免混入无关变更。
9. 风险清单
| # | 风险 | 等级 | 缓解 |
|---|---|---|---|
| 1 | 利息导致积分通胀 | 🔴 高 | 分段递减利率 + 全站产出上界测算(§1.2)+ 后台可随时改利率 / 关停 |
| 2 | 跨库不一致(扣了积分没进银行 / 反之) | 🔴 高 | 补偿写入 + 对账工具(§7.3) |
| 3 | 站长大额存款放大利息 | 🟠 中 | 分段利率(§3 决策点 2) |
| 4 | 贷款凭空造币 | 🔴 高 | 默认不做;若做则限定存单质押(§3 决策点 4) |
| 5 | 小号互转洗分 | 🟠 中 | 转账手续费 + 可加「同 IP 限制」(§3 决策点 5) |
| 6 | route_after_dispatch 每页触发的性能开销 | 🟠 中 | $_SESSION 节流(命中零查询)+ budget() 闸门 |
| 7 | 命名空间 / 目录名不一致 → Linux 500 | 🟠 中 | plugin_case_lint.py + 实测 class_exists |
| 8 | 三语键集合漂移 | 🟡 低 | 交付前跑键集合比对 |
| 9 | 浮点金额漂移 | 🟡 低 | 全整数(积分 + bp 利率) |
10. 待你确认的问题
- 决策点 1:单银行 还是 多银行?(建议 A 单银行)
- 决策点 2:利率模型选哪个?(建议 A 分段递减;若求零风险选 D 零利率)
- 若选 A,分段档位与 bp 值请你定,或我按 §1.2 测算给一套。
- 决策点 3:定期存款做不做?(建议 P3 做)
- 决策点 4:贷款做不做?(建议不做)
- 决策点 5:用户间转账做不做?(建议做,带手续费)
- 范围确认:P0–P2 先做最小闭环(活期 + 分段利率 + 流水 + 排行),可否?
- 命名确认:插件名「社区银行」/ 目录
community_bank/ 前缀cb_是否 OK?
本方案未写任何代码、未建任何表、未改任何文件(除本方案文档本身)。
拍板后建议从 P0 骨架 开始,按「阶段末暂停验收」的既有节奏推进。