【XIUNOX插件】论坛Vip插件 v1.5.1 [复制链接]

二级用户组

体验地址(有需要资源的欢迎注册):云盘资源库 - 资源集合地

yaoys_vip — VIP 会员插件

适配 XIUNOX(Xiuno BBS 4.0.4 重构版)的 VIP 会员系统:充值/续费、成长等级、红名徽标、每日成长值、隐藏内容特权。 当前版本 1.5.1 · 作者 yaoys · 只修改本插件目录,不动系统源码与其它插件。


1. 插件简介

面向会员运营的完整 VIP 体系,包含:

  • 购买:前台「我的VIP」页自助购买/续费,支持月付/季付/半年付/年付 4 种套餐、按份数叠加,扣费走系统 CreditsService(事务 + 行锁原子扣费)。

  • 等级:按累计成长值分 Lv1–Lv6 六级,等级决定徽标颜色与特权门槛。

  • 展示:全站多处用户名旁渲染 VIP 徽标(等级图标优先、文字胶囊回退),可自行替换图标图片。

  • 特权:VIP 可免除 xnx_hidden 隐藏内容解锁条件(开关控制)。

  • 管理:后台三 Tab(套餐设置 / 会员管理 / 会员历史),可搜索、手动改到期/成长值、一键取消会员。

依赖

  • 购买扣费依赖系统 lib/CreditsService.php(内置)。

  • 特权「免除隐藏内容解锁」依赖 xnx_hidden 插件(未安装则该特权不生效,功能仍可用)。

  • 其余功能无外部依赖。


2. 功能总览

2.1 前台功能

功能

入口

说明

我的VIP页

/?my_vip.htm(个人中心侧边栏「我的VIP」)

四 Tab:VIP状态 / 充值套餐 / 成长规则 / 开通历史

自助购买/续费

我的VIP → 充值套餐

htmx 提交,CreditsService::sub 原子扣费;可购买多份叠加

余额不足引导

购买流程

前端即时弹 Bootstrap Modal 引导「去充值 / 联系管理员」(链接后台可配);服务端 htmx 响应兜底返回同名 Modal

VIP 徽标

全站多处

楼层、帖子列表、用户主页、个人中心、主题详情楼主观/作者卡、首页侧边栏用户名旁

开通历史

我的VIP → 开通历史

分页展示本人开通/取消记录

积分记录页优化

/?my-credits.htm

历史 vip_purchase 显示为「开通VIP会员」,积分名替换为后台自定义名称

2.2 后台功能(插件设置 → yaoys_vip,仅 gid=1/2)

Tab

功能

套餐设置

总开关、充值货币、4 套餐价格/赠送成长值、每日成长值规则、等级阈值、等级徽标颜色、余额不足引导链接、隐藏内容特权开关

会员管理

搜索(用户名/UID)、VIP 用户列表(到期/成长值/等级/状态)、手动改到期与成长值、一键取消会员

会员历史

全站 VIP 开通/取消流水(分页)

2.3 会员特权

特权

后台开关

说明

会员红名徽标 + 等级图标

固定

Lv1–Lv6,颜色/图标按等级

每日登录 +成长值

grow_per_day

仅 VIP 生效,非 VIP 每日 -成长值

免除隐藏内容解锁

hidden_enable / hidden_vip_level

达标等级 VIP 直接查看 xnx_hidden 锁块内容

⚠️ 预留功能:后台设置页保留「特权:VIP 附件免费下载」卡片,但标注为「预留功能、无效」,红色提示。该功能曾实现后被取消,代码已整体回退,详见 第 10 节


3. 数据表

install.php 创建(前缀遵循 $db->tablepre,默认 bbs_),卸载时删除。

yaoys_vip — VIP 会员主表

字段

类型

说明

uid

int unsigned PK

用户 ID

grow

int unsigned

累计成长值

end

int unsigned

VIP 到期时间戳(0=非 VIP)

level

tinyint

当前等级(1–6),updateLevel() 维护

end 不写核心 bbs_user 表,统一存本表;is_vip = end > time()

yaoys_vip_log — 开通/取消日志

字段

类型

说明

id

int PK 自增

uid

int

用户

action

varchar(16)

buy / cancel

plan

varchar(16)

month/season/half_year/year

days / grow

int

本次获得的天数/成长值

end

int

到期时间戳

create_time

int

时间


4. 后台配置项(setting_get('yaoys_vip')

默认

说明

enabled

1

VIP 功能总开关

fee_type

3

充值货币:1=credits 2=golds 3=rmbs

month_fee / season_fee / half_year_fee / year_fee

1000/3000/6000/12000

套餐价格

month_grow / season_grow / half_year_grow / year_grow

10/50/100/300

套餐赠送成长值

grow_per_day

10

VIP 每日登录 +成长值

decrease_per_day

10

非 VIP 每日登录 -成长值

level2level6

3000…100000

等级成长值阈值

level_colors

1→#dc3545 … 6→#6f42c1

各等级徽标颜色(hex,可后台改)

hidden_enable

0

免除 xnx_hidden 解锁开关

hidden_vip_level

1

所需最低等级(0=任意 VIP)

recharge_url / contact_url

‘’

余额不足弹窗「去充值 / 联系管理员」链接(白名单校验,防 javascript:

套餐映射(YaoysVipService::$_yaoys_vip_plans):month=31 天、season=93、half_year=186、year=372。


5. 前台路由

注册于 hook/model_route_table_end.php + 分发于 hook/index_route_case_end.php

路由

文件

说明

my_vip

route/vip.php

我的VIP页(GET 渲染 / POST 购买),含频率限制(IP+uid 60 秒 5 次)

vip_icon

route/icon.php

VIP 等级图标输出(?vip_icon.htm&lv=N),带 1 天缓存头


6. 代码结构

plugin/yaoys_vip/
├── conf.json                     # 插件清单:版本 1.5.1、hooks_rank、overwrites_rank
├── install.php                   # 建表 + 默认配置(幂等)
├── uninstall.php                 # 删表 + 清理设置/缓存
├── upgrade.php                   # 迁移:清理废弃表 + 重跑 install.php
├── setting.php                   # 后台三 Tab 入口(套餐设置/会员管理/会员历史)
├── icon.png                      # 插件图标
├── model/
│   └── YaoysVipService.php       # ★ 核心服务(所有业务逻辑)
├── route/
│   ├── vip.php                   # 我的VIP页路由
│   └── icon.php                  # 等级图标输出路由
├── hook/                         # 15 个 hook 点(详见下节)
├── overwrite/view/htm/           # 覆盖系统模板(仅补 hook 注释标记)
│   ├── thread_list.inc.htm       # + <!--{hook thread_list_inc_username_after.htm}-->
│   └── sidebar_right.inc.htm     # + <!--{hook sidebar_user_username_after.htm}-->
├── view/htm/
│   ├── my_vip.htm                # 前台我的VIP页(四 Tab + 内联 JS)
│   ├── setting.htm               # 后台套餐设置
│   ├── vip_admin.htm             # 后台会员管理
│   └── vip_history.htm           # 后台会员历史
└── img/vip/                      # 等级图标 vip1.png ~ vip6.png(可选,缺则文字回退)

6.1 核心服务 model/YaoysVipService.php

方法

作用

getSettings() / saveSettings()

读/写设置(合并默认值;saveSettings 白名单 + intval;引导链接协议白名单)

creditTypeName($fee_type)

充值货币 → credits/golds/rmbs

vipStatus($uid)

读本表返回状态数组(含 is_vip

isVip($uid) / isVipUid($uid)

是否 VIP(后者走 60s 缓存 map)

activeVipMap()

活跃 VIP map(uid => level,CacheHelper 60s 缓存,防 N+1)

levelColor($level)

等级徽标颜色(hex 校验)

levelIconUrl($level) / levelIconHtml($level)

等级图标 URL/HTML(进程内缓存;走 vip_icon 路由规避静态目录拦截)

badgeHtml($uid, $compact)

VIP 徽标 HTML(图标优先,无图文字胶囊回退,盒子高度=文字高度)

buy($uid, $num, $type)

购买/续费:套餐校验 → 前置余额检查 → CreditsService::sub 原子扣费 → 延长到期/累加成长值 → 重算等级 → 清缓存 → 写日志

addLog() / logList()

写/查开通日志

cancelVip($uid)

一键取消(end=0 + 日志)

updateLevel($uid)

按成长值重算等级并写库

getMaxGrow($grow)

下一级阈值(进度条)

dailyGrow($uid)

每日成长值(VIP +,非 VIP -,下限 0)

vipLevelExempt($uid, $level)

等级豁免判断(level<=0 视为任意 VIP)

isAuthorOrStaff($uid, $gid, $threadUid)

作者/管理员/版主放行(遗留,配合隐藏内容)

replaceLockedBlock($message, $hiddenId, $replacement)

id="xnx-hidden-{id}" 平衡替换锁块

_queryActiveVipMap() / _clearCache()

内部:查询活跃 VIP / 清缓存

6.2 Hook 清单(conf.json hooks_rank,rank 同值按插件目录字母序)

Hook 点

rank

作用

model_inc_file.php

10

全局加载 YaoysVipService

model_route_table_end.php

10

注册 my_vip / vip_icon 路由

index_route_case_end.php

10

分发 my_vip / vip_icon 到插件路由文件

my_sidebar_nav_credits_before.htm

10

个人中心侧边栏加「我的VIP」入口

my_credits_page_start.htm

10

积分记录页:vip_purchase→「开通VIP会员」,积分名改后台自定义名

user_login_post_password_check_after.php

10

登录密码校验后捕获旧 login_date(供跨天判断)

user_login_post_end.php

10

登录后每日成长值(跨天幂等)

post_list_inc_username_after.htm

10

楼层用户名后 VIP 徽标

thread_list_inc_username_after.htm

10

首页帖子列表用户名后 VIP 徽标

thread_info_end.htm

10

帖子详情楼主名旁 VIP 徽标

thread_author_card_username_after.htm

10

详情右侧作者卡 VIP 徽标

sidebar_user_username_after.htm

10

首页侧边栏用户名后 VIP 徽标

user_profile_username_after.htm

10

用户主页卡 VIP 徽标

my_profile_username_after.htm

10

个人中心 VIP 徽标

model_post_format_end.php

0

VIP 免除 xnx_hidden 解锁:rank 0 低于 xnx_hidden 的 rank 1,在其渲染锁块后把锁块换为内容

6.3 Overwrite 文件(系统模板覆盖)

两个 overwrite 各只加了一行 hook 注释标记,用于暴露系统模板中不存在的 hook 点(不改其它任何内容):

  • overwrite/view/htm/thread_list.inc.htm:在帖子列表用户名后加 <!--{hook thread_list_inc_username_after.htm}-->

  • overwrite/view/htm/sidebar_right.inc.htm:在首页侧边栏用户名后加 <!--{hook sidebar_user_username_after.htm}-->

view/htm/ 不在 protected_paths 内,允许覆盖;系统升级若改这两个模板需人工同步。


7. 核心机制详解

7.1 VIP 状态判定与缓存

  • 状态以 yaoys_vip.end > time() 判定,不落 bbs_user 表。

  • 活跃 VIP 用 activeVipMap() 一次查出 uid=>level 后经 CacheHelper 缓存 60s,徽标渲染、接口放行均走该 map,避免 N+1 查询。

  • 写操作(购买/取消/成长值)后 _clearCache() 失效进程内缓存并 CacheHelper::pluginDeletePrefix('yaoys_vip')

7.2 等级成长体系

  • 成长值来源:购买套餐赠送 + 每日登录(VIP)。阈值 level2..level6,Lv1=0。

  • updateLevel() 按成长值区间定级,降级也生效(非 VIP 每日扣减会导致降级)。

  • 等级用于:徽标颜色/图标、vipLevelExempt() 特权门槛。

7.3 每日成长值(跨天幂等)

  • user_login_post_password_check_after.php 先捕获旧 login_date

  • user_login_post_end.php 判断 login_date 早于今天才执行 dailyGrow(),避免同日多次登录重复累加。

7.4 购买/续费流程与余额不足弹窗

  1. route/vip.php POST → 频率限制(IP+uid,60s/5 次)→ buy()

  2. buy():套餐/开关校验 → checkNegative() 前置余额检查(不足返回 code=insufficient_balance + 余额/需扣)→ CreditsService::sub() 事务+行锁原子扣费。

  3. 续费:有效期内 end+ 叠加;过期:重置到期 + 成长值累计;新用户:插入。

  4. 服务端 htmx 响应在余额不足时返回 Bootstrap Modal(引导「去充值/联系管理员」,链接来自 recharge_url/contact_url)。

  5. 前端 my_vip.htm 内联 JS:套餐 Tab 即时算应付/赠成长值、余额不足即时弹窗(不发请求)、XN.confirm 二次确认、htmx:afterRequest 失败恢复按钮。

7.5 徽标渲染(图标优先 / 文字回退)

  • levelIconUrl() 检测 img/vip/vip{N}.{png|svg|gif|jpg|jpeg|webp}(每级进程内缓存),有图返回 <img>(经 vip_icon 路由输出,规避 Web 服务器拦截 plugin/ 静态目录的 404)。

  • 无图回退文字胶囊 Lv{N},样式保证整个 span 盒子高度 = 一个文字高度padding:0 .35em + line-height:1),与用户名同大。

  • 颜色取 level_colors[level],Lv1 即普通 VIP 色。

7.6 VIP 免除隐藏内容解锁(依赖 xnx_hidden)

  • model_post_format_end.php(rank 0):hidden_enable 开启且等级达标时,对每个未解锁的隐藏块调用 HiddenService::renderHiddenContent() 生成内容,再用 replaceLockedBlock() 平衡替换锁块。

  • 只调用 xnx_hidden 的公开方法,不改 xnx_hidden 代码。

7.7 积分记录页优化

  • my_credits_page_start.htmcredits.htm 渲染前重写展示数据:历史 vip_purchase/raw:vip_purchase → 「开通VIP会员」;changes 中的 积分/金币/RMB → $conf['credits_name']/['golds_name']/['rmbs_name'] 自定义名。

7.8 等级图标路由

  • route/icon.php:按 lvimg/vip/vip{lv}.*,输出对应 MIME + 1 天缓存头;找不到 http_404()


8. 前台路由 URL 一览

场景

URL(取决于 url_rewrite 配置)

我的VIP

?my_vip.htm

等级图标

?vip_icon.htm&lv=3

购买提交

?my_vip.htm POST(htmx)


9. 安装 / 升级 / 卸载

  1. 安装:后台「插件 → 上传/启用」yaoys_vip,install.php 自动建表 + 写默认配置。

  2. 升级:上传新 zip 覆盖 → 后台重新启用(触发 plugin_clear_tmp_dir() 清编译缓存);upgrade.php 清理废弃表并重跑 install.php(幂等)。

  3. 卸载uninstall.phpyaoys_vipyaoys_vip_log 表 + 配置 + 缓存。

注意:改插件源码后 _include() 只在 tmp 编译产物缺失时重编译,需删除 tmp/ 下对应文件(或重启用插件)才会生效。


10. 预留功能(v1.5.1 取消)

「特权:VIP 附件免费下载 / 免回复下载」(原计划对接 xnx_attach_access 附件权限插件)——已取消,代码整体回退

  • 曾实现并评审的路径:overwrite route/attach.php 显式接管下载权限 + 前台按钮替换 + 购买接口防御,均已删除。

  • 原因:方案(无论「临时伪装管理员 gid=1」还是 overwrite 整文件副本)均不被接受,且有维护与边界风险。

  • 现状:后台设置页保留该卡片,标注红色「预留功能 / 无效 / 无实际作用」,开关与输入 disabled,不参与表单提交、不读任何设置键。

  • 后续如需接入:在设置卡片处实现即可,当前为预留开发位。


11. 开发与部署注意事项

  1. 不改系统源码:一切扩展走 hook + 非保护路径 overwrite(view/htm/ 等),禁止动 model/ route/ xiunophp/ lib/

  2. 积分名自定义:前台支付/余额显示优先用 $conf['credits_name']/['golds_name']/['rmbs_name'],勿硬编码 人民币/金币/积分。

  3. 扣费安全:一律走 CreditsService(事务+行锁);saveSettings 白名单 + intval;引导链接协议白名单防 javascript: 注入。

  4. 缓存:活跃 VIP map 60s;等级图标 URL 进程内缓存;改代码后清 tmp。

  5. 打包plugin/yaoys_vip/plugin/yaoys_vip.zip(zip 根目录含内层 yaoys_vip/),conf.json 版本号随变更递增驱动后台「需升级」徽章。



论坛Vip插件 截图

论坛Vip插件 截图

论坛Vip插件 截图

论坛Vip插件 截图

论坛Vip插件 截图



版本:v1.5.1
适用版本:1.1
开发者:姚先森

请登录后查看此内容
最新回复

请先登录后再回复 登录