[开发]星座运势插件(horoscope)开发方案

👑Lv.11 元老 🌞 骨干会员
2026-10-26 00:38:23

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/docs/星座运势插件开发方案_2026-10-18.md。

星座运势插件(horoscope)开发方案

日期:2026-10-18

状态:待拍板(4 个决策点见 §12,拍板后再动手)

依据:docs/插件开发规范.md v1.6(下称「规范」)、plugins/daily_checkin(每日一次型)、plugins/points_mall(A 派前台骨架)、plugins/quiz_duel(娱乐类)

图标与 API 已实测,见 §10.1、§10.2


一、定位与边界

一句话:给登录用户一个「每天来看一眼」的理由——今日运势、未来 7 天、星座配对,全部本地算法生成,零外部依赖。

它不是什么:

  • 不做真八字排盘(要农历/节气/干支历,工作量是本方案的 5 倍,且社区场景用不上)
  • 不接外部运势 API(可用性、限流、隐私、付费都是坑,见 §3.2)
  • 不做付费解锁(社区站没这个付费意愿,反而劝退打开)

和现有插件的关系(避免功能重叠):

已有插件重叠点本插件的差异
daily_checkin 每日签到都是「每天来一次」签到是得积分的确定收益;运势是看内容的软性留存。二者可互相导流,不冲突
dice 掷骰子都是随机娱乐dice 是「玩一把就走」,无个人化;运势按用户+日期定种子,每人每天不一样
xiuxian 修仙都是娱乐玩法修仙是长线养成;运势是轻量日更


二、产品形态

2.1 三个页面 + 两个注入点

类型路径说明
主页面GET /horoscope今日运势(核心页)
配对页GET /horoscope/pair星座配对(P1)
JSON 接口GET /horoscope/today?sign=aries切换星座时无刷新取数据
保存档案POST /horoscope/profile保存星座 / 生日(写操作)
后台GET /admin/horoscope设置 + 统计(两 Tab)

注入点:

钩子位置内容
nav_plugin_linksPC 左栏「应用」+ 顶栏「应用」下拉「星座运势」入口(双触发,规范 §9.3)
nav_user_menu_items用户下拉(PC + 移动)「我的星座」(双触发,按 placement 分渲染)
right_sidebar_after右栏用户面板之后今日运势小卡:星座 + 分数 + 一句话 + 「查看完整」

右栏卡只在登录后显示(与 daily_checkin 一致:游客没有用户面板,卡片上下文不成立)。

2.2 主页面结构(对齐 A 派骨架)

规范 §6.4-2 要求前台插件页统一 .mn-section。本插件跟 points_mall / community_bank 同款:

┌ .mn-section .hs-wrap ──────────────────────────────┐
│  🌙 星座运势                          [切换星座 ▾] │  ← h1.mn-fs-20.mn-fw-600 + icon('moon',18)
│  白羊座 · 3.21–4.19 · 10 月 18 日 周六              │  ← .hs-intro(13px / line-height 1.7)
│                                                     │
│  ┌ 今日综合 ────────────────────────────────────┐   │
│  │   88 分   ★★★★☆                              │   │
│  │   今天适合把拖了很久的事收个尾…                │   │
│  └──────────────────────────────────────────────┘   │
│  ❤ 爱情 82    💼 事业 90    🪙 财运 74    🌱 健康 86 │  ← 5 维度(综合单独强调)
│  🎨 幸运色 [■ 天空蓝]   🎯 幸运数字 7   ⭐ 贵人 天秤 │
│  宜:整理房间 · 主动联系    忌:熬夜 · 冲动消费      │
└─────────────────────────────────────────────────────┘
┌ 🌙 未来 7 天 ───────────────────────────────────────┐
│  7 根竖条(综合分),今天高亮                        │
└─────────────────────────────────────────────────────┘
┌ ❤ 星座配对 ─────────────────────────────────────────┐
│  白羊 × [下拉选择]  →  82 分 · 火 + 风,互相点燃     │
└─────────────────────────────────────────────────────┘

未设置星座时:主卡位置换成「选择你的星座」12 格选择器(点一下即保存),不做空页面。


三、核心设计:确定性运势引擎

这是整个插件的技术核心,也是它「不像随机数生成器」的关键。

3.1 为什么必须确定性

用户刷新页面结果变了 = 这东西是假的 = 明天不来了。所以:

同一用户 + 同一星座 + 同一天 → 结果永远一致;跨天自动变化。

实现:

// plugins/horoscope/Plugin.php
private static function seed(int $userId, string $sign, string $date): string
{
    // 用 hash_hmac 而不是 mt_srand():不污染同请求内其他随机逻辑,
    // 且跨进程/跨机器完全可复现(mt_* 是全局状态,PHP 版本间算法也变过)
    return hash_hmac('sha256', $date . '|' . $userId . '|' . $sign, self::salt());
}

为什么不用 mt_srand() + mt_rand():

  • mt_srand() 是全局状态,会污染同请求内其他插件的随机逻辑(dice、quiz_duel、red_packet 都在用随机)
  • hash_hmac 纯函数,给定输入必得同一输出,可以用夹具断言(这是能写测试的前提)

3.2 为什么不用外部 API

方案问题
免费星座 API大多需 Key + 限流;中文内容质量差;服务挂了整页白
抓取星座屋等站点侵权 + 反爬 + 页面结构一变就崩
付费 API站长成本,社区站无 ROI

本地算法的额外好处:不产生任何出站请求 → 不需要 net:fetch 权限,也不受生产机 disable_functions 影响(规范 §1 红线)。

3.3 派生规则

从 64 位 hex 种子按固定偏移切片,每段独立派生一个值:

字段派生方式取值范围
综合分55 + n % 4555–99
爱情 / 事业 / 财运 / 健康同上,各自独立偏移55–99
幸运色12 色池取模12 选 1
幸运数字1 + n % 91–9
贵人星座12 星座中排除自己后取模11 选 1
宜 / 忌各 20 条池取模,取 1 条20 选 1

分档:≥85 → high / ≥70 → mid / <70 → low,对应三套文案池。

盐:激活时随机生成 32 字节写入 settings(键 horoscope_seed_salt),只在不存在时写。

⚠️ 盐一旦丢失,全站用户运势全部重排。所以 activate() 必须写成「读不到才写」,且 uninstall() 才删。

3.4 换日边界

MVP 用服务器时区 date('Y-m-d'),凌晨 0 点换日。

P2 可加「换日时间」配置(如 04:00 换日,照顾熬夜党),但要同步改 todayDate() 单点函数,不能散落多处。


四、内容体系

4.1 维度与文案量

主流星座站(星座屋 / 今日运势等)的通行结构:综合 / 爱情 / 事业学业 / 财富 / 健康 + 指数 + 幸运色 / 幸运数字 / 贵人星座 / 宜忌。本方案取这套,减掉「商谈指数」「穿衣推荐」这类冷门项。

内容条数(每语)说明
5 维度 × 3 档 × 5 条75核心文案池
12 星座名 + 日期区间 + 元素12白羊座 / 3.21–4.19 / 火
12 星座性格一句话12用于「选择星座」引导与配对页
12 幸运色(名 + hex)12hex 走内联 CSS 变量(规范 §7.3-4 合规)
宜 / 忌 池40各 20 条
元素配对文案(10 组合 × 3 档)30见 §4.2
UI 文案(按钮/提示/后台表单)~70—
合计≈ 251 键/语三语共 ≈ 753 条

工作量集中在文案,不在代码。建议 P0 每档先写 3 条(45 条),P1 补满到 5 条——键名不变,只加条目,不返工。

4.2 配对模型(不用 78 组硬编)

12×12 配对的朴素做法是写 78 组文案,不可维护。用四元素模型(占星学标准做法,有据可依):

元素星座
火白羊 / 狮子 / 射手
土金牛 / 处女 / 摩羯
风双子 / 天秤 / 水瓶
水巨蟹 / 天蝎 / 双鱼

元素关系分数区间语义
同元素85–98天然同频
火+风 / 土+水75–92互相助燃 / 互相滋养
火+土 / 风+水55–70节奏不同,需磨合
火+水 / 土+风40–60差异最大,靠沟通

4×4 对称 → 10 种无序组合 × 3 档 = 30 条文案覆盖全部 78 组配对。具体分数由两星座索引做种子,落在区间内。


五、数据表

-- 用户星座档案
CREATE TABLE IF NOT EXISTS hs_profiles (
    user_id     INTEGER PRIMARY KEY,
    sign        TEXT NOT NULL,                 -- aries / taurus / ...
    birthday_md TEXT NOT NULL DEFAULT '',      -- 'MM-DD',只存月日,不存年份
    created_at  TEXT NOT NULL,
    updated_at  TEXT NOT NULL
);

-- 查看日志(仅登录用户,供后台统计)
CREATE TABLE IF NOT EXISTS hs_logs (
    id         INTEGER PRIMARY KEY,
    user_id    INTEGER NOT NULL,
    sign       TEXT NOT NULL,
    log_date   TEXT NOT NULL,                  -- Y-m-d
    action     TEXT NOT NULL,                  -- 'view' | 'pair'
    created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_hs_logs_date ON hs_logs (log_date);

设计取舍:

  • 不存每日运势结果。种子是 (日期, 用户, 星座) 的纯函数 → 历史任意一天都能重算,存了反而是冗余 + 可能不一致。
  • 不存生日年份。只需要月日推星座,存年份是没必要的隐私负担。
  • 游客不写 log(user_id 无 0 值),避免被刷表;游客浏览量走核心访问统计。
  • 表名前缀 hs_(horoscope),避免与其他插件撞名。

六、文件清单

plugins/horoscope/
├── plugin.json
├── Plugin.php               # 常量/星座表/DDL/种子引擎/档案读写/统计
├── FrontController.php      # index / pair / today(JSON) / saveProfile
├── AdminController.php      # index(设置+统计) / save
├── hook/
│   ├── init_after.php       # 惰性建表兜底
│   ├── route_register.php
│   ├── admin_route_register.php
│   ├── nav_plugin_links.php
│   ├── nav_user_menu_items.php
│   └── right_sidebar_after.php
├── views/
│   ├── index.php            # 今日运势
│   ├── pair.php             # 星座配对(P1)
│   ├── admin.php            # 后台两 Tab
│   └── _sidebar.php         # 右栏小卡
├── assets/
│   ├── style.css
│   └── script.js
├── lang/{zh,zh_tw,en}.php
└── data/                    # 自动生成(含 .htaccess / web.config)

plugin.json 草案:

{
    "name": "星座运势",
    "version": "1.0.0",
    "min_core": "1.1.26",
    "author": "FlintHub",
    "description": "星座运势:按用户与日期确定性生成今日运势(综合/爱情/事业/财运/健康)、幸运色与宜忌,支持星座配对与 7 天趋势",
    "activated": true,
    "category": "entertainment",
    "icon": "moon",
    "admin_url": "/admin/horoscope",
    "hooks": { ... },
    "permissions": ["route:admin", "system:settings"]
}

  • category: entertainment —— 与 dice / quiz_duel / xiuxian 同分类(10 类之一,规范 §3)
  • min_core: 1.1.26 —— 与当前核心同版本,避免老站装上后缺能力静默失效
  • 权限两项:route:admin(后台路由)+ system:settings(activate 写盐与开关)

七、后台功能

复用核心 .admin-tabs / .admin-tab-btn / .admin-table(规范 §11 后台 Tab 条),不自造 tab 类。

Tab 1 设置

项类型默认说明
模块开关开关开关闭后导航入口消失、路由 404、右栏卡不渲染
每日首次查看奖励数字00 = 不奖励;>0 时当天首次进入奖励积分
允许游客查看开关开关 = 未登录跳登录
默认星座下拉白羊未设置档案时的兜底展示

Tab 2 统计

  • 今日查看 / 7 日查看 / 累计查看
  • 12 星座分布(横条,纯 CSS 宽度)
  • 最近 20 条记录(用户 / 星座 / 动作 / 时间)

八、i18n 方案

  • 全部文案走 lang/{zh,zh_tw,en}.php,键名 plugin.horoscope.*
  • 占位符用 {key}(I18n::get($key, ['sign' => ...]),实测 app/Helpers/I18n.php:156)
  • 三语键集合与键序必须完全一致(验收标准,规范 §8)
  • 星座名 / 维度名 / 档位文案统一走 i18n,不在 PHP 里硬编码中文

⚠️ 这决定了 Plugin.php 里只能存「键名索引」,不能存成品文案。例:

```php

private const TEXT_KEYS = [

'love' => ['high' => ['text.love_high_1', ...], 'mid' => [...], 'low' => [...]],

];

```


九、分期计划

P0 — MVP(可上线)

  1. Plugin.php:星座表、DDL、种子引擎、档案读写、统计查询
  2. 今日运势页(5 维度 + 幸运色/数字/贵人 + 宜忌)
  3. 星座档案(12 格选择器 / 填生日自动识别)
  4. 右栏小卡 + 导航入口 + 用户下拉入口
  5. 后台设置 + 统计
  6. 三语各 ~200 键
  7. 自检 + 审计

P1 — 增强

  1. 星座配对页(§4.2 模型)
  2. 未来 7 天趋势条
  3. 文案池补满(每档 3 → 5 条)

P2 — 可选

  1. 分享图(canvas 绘制 → toDataURL;CSP 不含 blob:,必须用 dataURL)
  2. 塔罗 / 抽签(独立子模块,不影响主流程)
  3. 移动端 API(mobile_api_routes)
  4. 换日时间可配置

十、已核验的技术事实

10.1 图标(双重核验:$map 有名字 + 字体子集有字形)

用途图标名码点map字体
插件主图标 / 页面标题moonU+F186✅✅ gid=214
综合运势starU+F006✅✅
爱情heartU+F004✅✅
事业user-tieU+F508✅✅
财运coinsU+F53A✅✅
健康seedlingU+F4D8✅✅
幸运色paletteU+F53F✅✅
幸运数字bullseyeU+F140✅✅
配对hand-holding-heartU+F4BE✅✅
趋势chart-lineU+F201✅✅

不可用(已实测排除):briefcase(字体无字形)、circle-info / sparkles / hashtag / clover / cloud-sun($map 无此名)。

12 星座符号(♈♉♊…U+2648–U+2653)走系统字体,不依赖图标字体,可直接当文本输出。

10.2 API 签名(已读源码确认)

API签名备注
Auth::getCurrentUser()?array游客返回 null
Csrf::verifyOrDie()(string $token): void必须传参:$_POST['csrf'] ?? '';AJAX 走 HTTP_X_CSRF_TOKEN
I18n::get()(string $key, array $params = []): string占位符 {key};缺键返回 key 本身
Points::award()(int $userId, int $points, string $reason, int $relatedId = 0, string $relatedType = '')若启用奖励则用
Plugin::ensureSchema()(string $name, string $ddl)幂等建表

10.3 钩子约束(规范 §9.0 / §9.3)

  • 渲染钩子禁 die() / exit() / header()
  • nav_plugin_links / nav_user_menu_items 一次渲染触发两次 → 钩子文件禁止声明顶层函数/类(否则降级 include_once,第二处入口静默消失)
  • nav_plugin_links 图标 16px($view->icon('moon', 16),与 13 个既有实现一致)
  • nav_user_menu_items 图标 14px,按 placement 分 pc / mobile 渲染

十一、红线自查清单(写代码时逐条对照)

#红线本方案的处理
1目录名含下划线 → 生命周期静默失效horoscope 无下划线,但仍加 class_alias 防御
2activate() 可能被跳过 → 无表init_after 惰性建表兜底
3写操作顺序先 requireLogin(),后 Csrf::verifyOrDie()
4渲染钩子中断页面所有钩子只 echo,不 exit
5零内联无 on*=、无静态 style=(幸运色用 CSS 变量注入,合规)、无内联 <script>
6图标问号全部图标已双重核验(§10.1)
7i18n 键漂移三语键集合 + 键序一致性纳入验收
8SQL 注入全部 prepare() + 绑定;无字符串拼接
9前台链接硬编码一律 $this->url() / 拼 BASE_PATH
10资源缓存?v=filemtime()
11双主题可读性只用 --mn-* 变量,不写死颜色
12时区/换日单点 todayDate() 函数,不散落


十二、待拍板的 4 个决策点

决策 1:积分怎么处理

选项说明代价
A(推荐) 不涉及积分纯内容模块,零耦合无
B 每日首次查看 +2 积分拉活跃,但要防刷(一天一次幂等)多一张 hs_logs 唯一约束 + 奖励逻辑
C 消耗积分看深度内容商业化思路社区站大概率劝退,不建议

推荐 A 起步。若选 B,实现上就是把 hs_logs 的 (user_id, log_date, action='view') 做唯一索引,插入成功才发奖。

决策 2:内容边界

选项内容工作量
A(推荐) 星座运势 + 配对今日 + 7 天 + 配对基准
B 只做今日运势最轻-30%
C + 塔罗 / 抽签多一套 64 签文案+80%

决策 3:游客可见范围

选项说明
A(推荐) 游客可看「默认星座」的今日运势,看不到个性化与 7 天趋势保留 SEO 与转化入口
B 全开(游客也能选星座)无法记档案,只能存 session
C 全关(未登录跳登录)少一个 SEO 入口

决策 4:文案由谁写

选项说明
A(推荐) 我按占星通行话术写,三语各 ~250 键,P0 先写 3 条/档可控、可改、零依赖
B 你提供文案,我留占位质量最高,但要你先写 750 条


十三、验收标准

P0 交付时必须同时满足:

  1. php -l 全绿(8.0 + 8.2 双版本)
  2. 三语键集合与键序完全一致(脚本比对,零差异)
  3. 确定性断言:同一 (用户, 星座, 日期) 连算 1000 次结果完全一致;跨日期必变
  4. 分布断言:1000 个用户同一天同一星座,5 维度分数落在 55–99 且不是常数
  5. HTTP 冒烟:/horoscope 200、未登录跳转符合决策 3、右栏卡只在登录后出现、on*= 为 0
  6. 审计工具(plugin-audit/cli/audit.php):0 CRITICAL / 0 MAJOR
  7. 双主题走查(浅色 + 暗夜星辰)
  8. 说明.md + plugin.json.changelog 同步

附:与「网上参考」的差异说明

主流星座站(星座屋、今日运势等)普遍包含:综合 / 爱情 / 事业学业 / 财富 / 健康指数、幸运色、幸运数字、贵人星座、宜忌、穿衣推荐、商谈指数。

本方案取前 7 项,去掉后 3 项:

  • 穿衣推荐 → 与「幸运色」重复
  • 商谈指数 → 社区用户用不上
  • 每日生肖 → 与星座体系混搭,显得杂

保留的判断依据:社区站要的是「一眼看完 + 明天还想来」,不是「专业占星报告」。

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

全部回复 (0)

暂无回复

请 登录