[开发] 周易起名插件 · 开发规划方案

👑Lv.11 元老 🌞 骨干会员
2026-10-30 23:04:49

版本:v1(规划稿,未动代码) 日期:2026-10-30 依据:docs/插件开发规范.md v1.7(49 钩子全表)+ 原站 6 个前端资源实测 目标目录:plugins/qiming/(目录名小写无下划线 → 无需 class_alias) 当前核心版本:V1.3.0(version.php)→ min_core 取 1.3.0


一、素材盘点(实测结论)

1.1 下载结果:6/6 全部拿到

原站 blog.free.je 有 slowAES cookie 挑战(/aes.js + __test cookie),裸 curl 只能拿到挑战页。 已用 PHP 解 AES-128-CBC 算出 cookie 后完整下载,落在 .workbuddy-ai/tmp/zyqm/:

文件大小内容可复用性
js/lunar.js4,225 B农历↔公历(1900–2100 位压缩表 201 项)✅ 算法可移植
js/bazi.js15,434 B四柱、节气(Meeus 太阳黄经迭代)、旺衰、喜用神、通关✅ 算法可移植(核心价值)
js/dict.js10,545 B168 字字库 + 53 重名 / 76 多音 / 45 忌字黑名单⚠️ 结构可借鉴,数据建议自建
js/names.js9,364 B名字组合 + 打分排序 + 寓意文案拼装✅ 打分逻辑可移植
js/app.js9,524 BDOM 绑定与渲染(document.getElementById 硬绑)❌ 不可复用,需重写
style.css18,471 B道教风视觉(流云 / 八卦盘 / 水墨)❌ 与 FlintHub 主题不兼容,仅取配色灵感

挑战机制备忘:document.cookie="__test="+toHex(slowAES.decrypt(c,2,a,b)),mode 2 = CBC、key/iv 各 16 字节。 复现脚本已留在 .workbuddy-ai/tmp/zyqm/fetch.php(本机 SSL 需 CURLOPT_SSL_VERIFYPEER=false)。

1.2 代码质量评估

四个业务 JS 全是 UMD 自包含、零依赖,末尾统一 module.exports / global.Xxx, 说明原作者就是按「可移植库」写的 —— 这对我们是利好:

lunar.js  → LunarConvert = { lunar2solar, leapMonth }
bazi.js   → Bazi = { compute, gregorianJD, sunLongitude, termJD, getMonthBranch, getDayPillar, GAN, ZHI, WX, ... }
dict.js   → NameDict = { DICT, BANNED, POLYPHONE, TABOO_WORD }
names.js  → NameGen = { generateNames, buildPools, buildMeaning }

bazi.js:154 compute() 的入参是纯对象 {year,month,day,hour,minute,isLunar,leap}, 出参是完整结构体(四柱 + counts + hidden + favorable + taboo + tongguan + analysisText), 没有任何 DOM 依赖 → 这是可以逐行移植成 PHP 的。

1.3 字库实测数据(决定后续要不要补字)

DICT 条目 168 字 | BANNED 53 | POLYPHONE 76 | TABOO_WORD 45
五行分布(木/火/土/金/水)= 32 / 33 / 31 / 33 / 39   → 均衡,可用
性别分布(通用/男/女)   = 110 / 0 / 58              → ⚠️ 男性专用字 0 条

这是原站的硬伤:names.js:38 的过滤是 if (g !== 0 && g !== gender) continue, 选「男」时只能在 110 个通用字里挑,出不来「阳刚」气质(对比女名有 58 个专用字)。 结论:若要做男孩起名,必须补 60–80 个男性专用字,否则男女体验不对称。

1.4 版权与合规提示(必须先定调)

素材性质建议
农历表 / 节气算法 / 日柱甲子公开算法(solarlunar、Meeus 天文公式),无版权争议可自行实现
168 字库 + 每条「寓意 + 出处」文案他人整理,有独创性表达⚠️ 建议自建字库(重写文案、扩到 300–500 字),不要整段照搬
页面 CSS / 八卦 SVG / 文案他人美术与文案作品❌ 不复用,按 FlintHub 设计语言重做

这条不影响技术方案,但影响「能不能公开上架到插件商店」。如果只是自己站内用,风险低; 如果要投稿到 market_hub,字库必须自建。


二、需求边界

2.1 做

  1. 四柱排盘:公历 / 农历输入,年柱以立春、月柱以十二节为界,日柱甲子历差,时柱五鼠遁(含晚子时 23:00 特殊处理)。
  2. 五行旺衰与喜用神:子平扶抑 + 从格识别 + 通关神 + 月令禁忌,明确输出「喜用 / 忌神 / 通关 / 缺而不补」四类结论。
  3. 起名推荐:≥5 组双字名,按喜用神覆盖度 + 阴阳 + 音律 + 出处打分排序,标注「强烈推荐」。
  4. 换一批:同命局不同候选(salt 打散),不重新排盘。
  5. 起名记录:登录用户可保存本次结果,可在「我的起名记录」回看 / 删除。
  6. 后台配置:模块开关、游客权限、每次消耗积分、每日次数上限、默认姓氏性别、结果条数。

2.2 不做(明确砍掉,避免做半成品)

原站有的为什么不做
「姓名测试」服务格原站只是锚点到表单,本身没实现;且五格剖象打分属伪科学,做了反而掉档次
公司起名 / 成人改名逻辑同构,但字库与规则完全不同(公司名要避重名、要行业字),塞进来会做成四不像
康熙字典查询需要完整字典数据(几 MB),插件包会被 market_submit_max_kb(5 MB)卡住
AI 解名 / 大师咨询需外呼(net:fetch)+ 计费,属另一个产品形态,留给 ai_assistant 联动

服务宫格里保留「宝宝起名 / 八字起名 / 诗词起名」三项即可,都指向同一个表单(原站也是这么做的)。


三、架构决策(这一节需要你拍板)

方案 A:纯前端照搬

把 4 个 JS + 自写 CSS 塞进 assets/,PHP 只输出页面骨架。

  • ✅ 改动最小,算法零风险(原样跑)
  • ❌ 结果不可记录、不可分享、不可计费 —— 插件退化成「搬了个静态页进来」
  • ❌ app.js 里全是中文硬编码(alert('请填写姓氏')),i18n 门禁过不去
  • ❌ 168 字库 + 算法全量下发浏览器(约 40 KB),首屏多 5 个请求
  • ❌ 与规范 §7.3「零内联」冲突:app.js 里 style="color:..." 拼字符串,需大改

方案 B:PHP 服务端计算(推荐)

lunar.js / bazi.js / names.js 逐行移植为 PHP 类,页面 SSR。

  • ✅ 结果天然可记录、可分享(URL 带参数)、可计费
  • ✅ 零 AJAX,少一整套错误路径(JSON 解析失败 / 网络中断 / 竞态)
  • ✅ SEO 友好(起名页是典型的长尾搜索入口)
  • ✅ i18n 可控(文案在语言包,不在 JS 里)
  • ⚠️ 风险 = 移植偏差 → 用「node 跑原 JS 出基准 → PHP 逐字段比对」硬门槛兜住(见 §九)

方案 C:PHP 算八字 + 前端 JS 只做表单联动

本质是 B 的子集(B 也需要 60 行 JS 做农历闰月/天数联动),不单列。

推荐 B。代码量估算:算法层约 900–1100 行 PHP,其余(控制器/视图/后台/语言包)约 1200 行。


四、目录结构

plugins/qiming/
├── plugin.json               # name/version/min_core/category=entertainment/icon=book
├── Plugin.php                # db() / ddl() / activate / deactivate / uninstall + 配置读写 + 记录 CRUD
├── FrontController.php       # GET /qiming(表单+结果)· POST /qiming/save · GET /qiming/history · POST /qiming/history/delete
├── AdminController.php       # GET /admin/qiming · POST /admin/qiming/save
├── Lunar.php                 # 农历↔公历(1900–2100 表 + 闰月 + 每月天数)
├── Bazi.php                  # 儒略日 / 太阳黄经 / 节气迭代 / 四柱 / 旺衰 / 喜用神 / 通关
├── NameDict.php              # 字库(自建,结构同原站:[字,拼音,声调,五行,性别,寓意,出处,阴阳])+ 三类黑名单
├── NameGen.php               # 组合生成 + 打分排序 + 寓意文案拼装
├── 说明.md                    # 插件说明(版本行 + 功能 + 配置项 + 免责声明)
├── hook/
│   ├── init_after.php        # 建表兜底(戳文件门控,见 §七)
│   ├── route_register.php    # 前台路由
│   ├── admin_route_register.php
│   ├── nav_plugin_links.php  # 「应用」入口(PC 左栏 + 顶栏下拉,双触发)
│   └── nav_user_menu_items.php  # 用户下拉「我的起名记录」(按 placement 分支)
├── views/
│   ├── index.php             # 表单 + 四柱 + 五行 + 名字列表(单页)
│   ├── history.php           # 我的起名记录
│   └── admin.php             # 后台配置
├── assets/
│   ├── style.css             # 全部 --mn-* 变量,禁硬编码色
│   └── script.js             # 只做:农历闰月/天数联动 + 平滑滚动 + 复制名字
├── lang/
│   ├── zh.php  ├── en.php  └── zh_tw.php
└── data/                     # 运行时自动生成(.htaccess / web.config 由核心补)

为什么算法类平铺在插件根:Autoloader.php:41-48 对 Plugin\ 只做「插件目录 + 剩余路径」拼接, 子目录可用但会引入 Linux 大小写风险(Lib/ vs lib/);现有插件全部平铺,跟随惯例最稳。


五、数据模型

配置走核心 Settings(量小、要后台可视化、要随卸载清理);记录走插件独立库。

5.1 独立库 plugins/qiming/data/qiming.sqlite

-- 起名记录
CREATE TABLE IF NOT EXISTS qm_records (
    id          INTEGER PRIMARY KEY,
    user_id     INTEGER NOT NULL DEFAULT 0,
    surname     TEXT    NOT NULL DEFAULT '',
    gender      INTEGER NOT NULL DEFAULT 0,   -- 1 男 / 2 女
    calendar    TEXT    NOT NULL DEFAULT 'solar',
    birth_y     INTEGER NOT NULL DEFAULT 0,
    birth_m     INTEGER NOT NULL DEFAULT 0,
    birth_d     INTEGER NOT NULL DEFAULT 0,
    birth_h     INTEGER NOT NULL DEFAULT 0,   -- 0/1/3/5/.../23 时辰起始小时
    is_leap     INTEGER NOT NULL DEFAULT 0,
    bazi_json   TEXT    NOT NULL DEFAULT '',  -- 四柱 + 喜用神快照
    names_json  TEXT    NOT NULL DEFAULT '',  -- 本次推荐的名字数组
    created_at  INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_qm_rec_uid ON qm_records (user_id, created_at DESC);

-- 每日次数(防刷 / 计费)
CREATE TABLE IF NOT EXISTS qm_usage (
    user_id INTEGER NOT NULL,
    day     TEXT    NOT NULL,                 -- 'YYYY-MM-DD'
    cnt     INTEGER NOT NULL DEFAULT 0,
    PRIMARY KEY (user_id, day)
);

⚠️ SQLite 限额坑(记忆里踩过):WHERE cnt + :a <= :lim 这种「表达式 vs 参数」在 SQLite 里 因类型序 INTEGER < TEXT 恒真,限额静默失效。正解:上限在 PHP 侧算好,用 bindValue(PARAM_INT)。

5.2 核心 Settings 键(qm_ 前缀,SETTING_DEFAULTS 白名单)

键默认说明
qm_enabled1模块总开关(关 → 路由 404、导航不出入口)
qm_guest1游客是否可起名(关 → requireLogin())
qm_cost_points0每次起名消耗积分(0 = 免费),走 Points::deduct()
qm_daily_limit0每日次数上限(0 = 不限)
qm_name_count6每批推荐条数(5–10)
qm_default_surname李表单默认姓氏
qm_default_gender2表单默认性别

activate() 里逐键 Settings::update()(内部会 buildCache), uninstall() 里先 DROP TABLE → 再清 settings 键 → 再 Settings::buildCache()(规范 §11.16)。


六、路由与钩子

6.1 前台路由(hook/route_register.php)

方法路径动作说明
GET/qimingindex无参 = 表单;带参 = 结果页(同一路由,可分享/可刷新/可后退)
POST/qiming/savesave保存记录(先 requireLogin() 后 Csrf::verifyOrDie())
GET/qiming/historyhistory我的起名记录(需登录)
POST/qiming/history/deletedeleteRecord删除((int) 强转 + user_id 归属校验防 IDOR)

结果页走 GET 参数,形如:

/qiming?s=李&g=2&c=solar&y=2026&m=10&d=30&h=4&l=0&salt=0
  • 可分享、可收藏、可后退、刷新不弹「重新提交表单」
  • 「换一批」= salt+1 的普通链接,零 AJAX
  • 参数非法(年份越界 / 农历闰月不存在)→ 不报错、不 500,回落到表单页并给一条提示

/qiming 与站点根真实目录无冲突(已核实根目录无同名文件/目录,规范 §6.2.2 第 7 条)。

6.2 后台路由(hook/admin_route_register.php)

$router->get('/qiming',       ['\Plugin\Qiming\AdminController', 'index']);   // 实际 /admin/qiming
$router->post('/qiming/save', ['\Plugin\Qiming\AdminController', 'save']);

⚠️ 不写 /admin 前缀(规范 §11.22,写了会变 /admin/admin/qiming → 404)。

6.3 钩子清单

钩子用途坑
init_after建表兜底(activate() 可能被静默跳过)用戳文件门控,别每请求跑 DDL
route_register前台路由先 if (!isset($router)) return;
admin_route_register后台路由需声明 route:admin
nav_plugin_links「应用」入口一次渲染触发两次(左栏带 __nav_uri、顶栏不带)→ 高亮必须 !empty($__nav_uri) 守卫;文件内禁声明顶层函数/类;图标 16px
nav_user_menu_items「我的起名记录」双触发(placement='pc' / 'mobile')→ 按 $placement 分别渲染;图标 14px(mn-fs-14)

不注册 layout_head_end:插件页样式走视图 section('css') 更精准,也避免每页白跑。 不注册 right_sidebar_after:右栏面板要「每页必跑」,会顶破「单插件每请求 hook 查询 ≤ 5」红线。

6.4 plugin.json

{
  "name": "周易起名",
  "version": "1.0.0",
  "min_core": "1.3.0",
  "author": "FlintHub",
  "description": "周易起名:四柱排盘(立春/十二节定界)、子平旺衰与喜用神、通关神推演,按喜用神与音形义生成宝宝名字,支持记录与分享",
  "activated": false,
  "category": "entertainment",
  "icon": "book",
  "sort_order": 0,
  "admin_url": "/admin/qiming",
  "hooks": { "...": "..." },
  "permissions": ["route:admin", "system:settings"]
}
  • icon = book,已 grep "'book'" app/Core/TemplateCompiler.php 命中(compass 已被 yijing_divination 占用,避免撞脸;备选 pen / magic / seedling 也都在表内)
  • 不需要 net:fetch(纯本地算法,零外呼)

七、前台页面结构

单页三段式(对齐 horoscope / points_mall 的范式):

<div class="mn-section qm-wrap">
  ① 表单区    .qm-form      姓氏 / 性别 / 历法 / 年月日 / 闰月 / 时辰
  ② 四柱区    .qm-pillars   年柱 月柱 日柱 时柱(五行色条)+ 日主一句话
  ③ 五行区    .qm-wuxing    五行分布条 + 喜用/忌/通关标签 + 分析文字 + 免责声明
  ④ 名字区    .qm-names     N 组名字卡(大字 + 拼音 + 五行 + 寓意 + 出处)+「换一批」
</div>

7.1 必须遵守的容器与节奏(规范 §6.4-2 / §6.4-5)

.qm-wrap { max-width: 1080px; margin: 0 auto; padding: 16px 20px 24px; }
.qm-wrap > * + * { margin-top: 18px; }          /* 容器统一纵向节奏,不逐块写 margin */
.qm-wrap > :last-child { margin-bottom: 0; }
@media (max-width: 860px) { .qm-wrap { padding: 14px 14px 18px; } }  /* 收窄,不是归零 */

这三条是 yijing_divination 一页连踩三处「卡片边框叠成一条」后总结出来的,直接照抄。

7.2 主题变量(规范 §11.7)

  • 五行配色用插件自定义变量,但底色/文字/边框一律 --mn-*: --qm-wood / --qm-fire / --qm-earth / --qm-metal / --qm-water 五个语义色, 浅色 fallback 给足,暗夜星辰下自动跟随 --mn-bg-card / --mn-text。
  • 禁:background:#fff、color:#333、硬编码 #0066cc。

7.3 零内联

  • 全部样式进 assets/style.css,全部交互进 assets/script.js
  • 五行色条宽度属动态 CSS 变量注入(style="--qm-pct:72%"),属规范 §7.3-4 允许的合规写法
  • 农历联动所需的「每年闰哪月 / 每月天数」以 data-* JSON 承载(htmlspecialchars(json_encode(...), ENT_QUOTES)),JS 端 JSON.parse

7.4 script.js 只做三件事(约 60–80 行)

  1. 历法切「农历」时显示闰月复选框
  2. 年份/月份变化时,按 data-* 里的表重算「日」下拉的可选上限,并把不存在的闰月 disabled
  3. 「复制名字」按钮(navigator.clipboard + 降级 execCommand)

无 JS 也能用:服务端会把所有选项渲染出来,提交时服务端再校验一次日期合法性。


八、i18n 策略

三份语言包,键集合必须完全一致(fh_i18n_check.php 门禁):

  • zh.php:完整
  • en.php / zh_tw.php:UI 文案翻译(标题、按钮、提示、免责声明、配置项)
  • 命理术语与名字寓意保留中文(「甲木」「喜用神」「通关」翻译成英文反而看不懂,且字库寓意本身是中文数据)→ 在 说明.md 与页面免责声明里写明这一点

键前缀统一 plugin.qiming.*;前台交互提示用 *_act 后缀,后台表单校验用 err_*。


九、分阶段实施(每阶段结束停下确认)

P0 · 骨架(不写业务逻辑)

plugin.json + Plugin.php(ddl/activate/uninstall/配置读写)+ 4 个钩子 + 空视图 + 后台配置页。

验收:激活成功 → 独立库建表 → /qiming 返回 200 → /admin/qiming 返回 200(非 404)→ 导航入口出现且两处都有(左栏 + 顶栏下拉)。

P1 · 算法层 + 一致性对照(最关键的一步)

移植 Lunar / Bazi / NameDict / NameGen 四个类。

验收(硬门槛):

  1. 用 node 跑原 4 个 JS,对样本集输出 JSON 基准
  2. 样本集 = 1910–2045 每年 01-01 / 06-15 / 12-31 × 12 时辰(共约 4900 组) + 立春前后 ±1 天(年柱切换边界)+ 12 个节气交界日(月柱切换)+ 23:00 晚子时(日柱/时干特殊分支)+ 闰月日期
  3. PHP 跑同样输入,逐字段比对:年/月/日/时 干支、counts[5]、hidden[5]、favorable、taboo、primaryYong、secondaryYong、tongguan、pattern、strength、score
  4. 零差异才算通过。任一处不一致 → 先定位是浮点精度(节气迭代 8 次收敛够不够)还是整数取模方向,不许「大致一致」放过

P2 · 前台页面

表单 + 四柱 + 五行 + 名字列表 + 换一批,双主题走查。

验收:/qiming 带参渲染 200;五行色条、名字卡、免责声明齐全;浅色/暗夜星辰各走一遍; grep -c '&#xf128;' 为 0(无问号图标);窄屏 375px 不贴边、卡片间距不为 0。

P3 · 记录与后台

保存 / 历史 / 删除 / 后台 7 项配置。

验收:保存 → 历史可见 → 删除后消失;换一个 uid 访问别人的记录 id → 404 或空(IDOR); 后台保存后前台立即生效(验证 Settings::update 的 buildCache 链);卸载后 qm_* settings 键清零、表已删。

P4 · 增强(可选,按需)

  • 积分消耗(Points::deduct(),不直写 users.points)
  • 每日次数上限(qm_usage + PHP 侧算上限)
  • 「分享到论坛」——⚠️ 风险最高的一项:核心没有公开的「插件代发帖」服务方法, 直接写 topic_index 会破坏分片一致性。建议改成「复制分享文案 + 跳转发帖页预填标题」,不做代发。

十、规范红线自查(针对本插件逐条落)

规范条款本插件落点
§4.1 目录名含下划线 → class_aliasqiming 无下划线,不需要
§6.4-4 图标必须在 $map 内book 已核实命中;渲染后断言页面无 &#xf128;
§9.3 nav_plugin_links 双触发文件内禁声明函数/类;!empty($__nav_uri) 守卫;16px
§9.1 #10 nav_user_menu_items 双触发按 placement 分支;14px
§11.22 后台路由不写 /admin/qiming,admin_url 写 /admin/qiming
§11.24 渲染钩子里 $this 不可用钩子内一律 htmlspecialchars(...) + Csrf::token()
§11.28 登录链接 / 写操作顺序用 Auth::loginUrl();先 requireLogin() 后 Csrf::verifyOrDie()
§6.2.1 URL 双模式路由用 $this->url() / Url::to();资源用 $bp;JS 用 window.FH_WEB
§7.3 零内联视图/钩子零 <style> 零 on*;动态值走 CSS 变量注入
§11.7 主题变量只用 --mn-* + 自定义 --qm-* 语义色
§11.4 性能红线钩子内零 SQL(只读 Settings 缓存);重逻辑全在插件页面内
§11.16 卸载清缓存uninstall() 里 Settings::buildCache()
§12.1-4 全角标点紧跟 $var消息串一律 {$var};交付前跑 php_var_cjk_check.php
§12.1-6 同命名空间漏 use交付前跑 php_missing_use_check.php
§12.1 零内联审计切片后台页从 admin-content 切到插件自己那行 </script>
§11.19 编码卫生UTF-8 无 BOM + LF;php -l 全过
§11.34 Linux 大小写跑 plugin_case_lint.py

十一、风险清单

#风险影响应对
1算法移植偏差排盘结果错 → 插件核心价值归零,且用户不会发现(看起来很专业)§九 P1 的一致性对照,4900+ 样本逐字段比对,零差异才继续
2节气浮点精度立春当天出生的人年柱可能错一位迭代次数 8 次收敛(原站如此),对照测试必须覆盖立春 ±1 天
3字库版权上架商店被投诉 / 下架自建字库,重写寓意文案;先做 200 字,后续可扩
4男性字缺失男孩起名效果明显弱于女孩补 60–80 个男性专用字(原站 0 条)
5命理内容合规平台/监管对「算命」敏感页面固定免责声明「仅供参考,不构成命理定论」;定位为「文化起名工具」而非「算命」;不做付费改运类话术
6隐私出生时间进 URL 与访问日志结果页参数只含年月日时(无姓名全称、无身份证级信息);提供「保存记录」时明确告知存哪
7分享到论坛代发帖破坏 SplitDB 分片一致性不做代发,改为「复制文案 + 跳转发帖页」

十二、需要你拍板的 4 件事

  1. 架构:走方案 B(PHP 服务端计算)还是方案 A(纯前端照搬)?我推荐 B。
  2. 字库:自建(重写 200 字,含男性字)还是先照搬原站 168 字跑通再说?
  3. 计费:是否要「每次起名消耗积分」?(要的话 P3 一起做,Points::deduct)
  4. 命名:目录 qiming、路由 /qiming、显示名「周易起名」—— 还是要叫「宝宝起名」/「八字起名」?

拍完这 4 项我就按 P0 开始,每阶段结束停下来给你验。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 7 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录