xnx_avatar_frame 头像框
头像框商店、购买、佩戴、过期管理插件,通过 avatar_component_frame.php hook 渲染装饰性头像框,零侵入核心头像组件代码。
功能特性
头像框管理:后台新建 / 编辑 / 删除头像框,支持名称、描述、排序、上下架开关
两种框类型:图片框(上传 PNG / GIF / JPG / WebP,自动校验真实 MIME)与 CSS 样式框(管理员内联 style 输出,已过滤 PHP 标签字符)
头像框商店:前台
/frame-shop.htm浏览可购买头像框,按 sort 排序,支持分页购买扣费:调用
CreditsService::sub()扣积分(支持 credits / golds 两种货币),事务由 CreditsService 内部保证;已拥有、用户组不在白名单、余额不足均会拦截佩戴管理:同一时间只能佩戴一个头像框;前台
/frame-my.htm查看已拥有头像框、佩戴 / 取消佩戴,自动记录佩戴日志过期管理:购买时按
duration_days(0=永久)计算expire_time;每日 cron(model_cron_daily_end.phphook)批量清理过期佩戴 + 显示时懒检查(getWearingFrame命中过期立即卸下并写日志)双重保障用户组限制:每个头像框可配置
allowed_gids白名单(逗号分隔,空 = 所有组可购买)头像框渲染:通过
avatar_component_frame.phphook 注入到avatar_component_from_data()内部,覆盖模式$data['frame_html'] = ...;图片框用绝对定位<img>叠加,CSS 框用绝对定位<div>叠加,均pointer-events:none不影响头像交互性能优化:
FrameService内置$userWearCache/$frameCache请求级静态缓存,帖子列表页同用户多次调用只查一次库,避免 N+1
安装说明
将插件目录放入
plugin/xnx_avatar_frame/进入后台「插件管理」找到「头像框」点击安装,自动建 3 张表并初始化默认配置
安装后默认启用,可在后台「插件设置」→「全局设置」中关闭
与 xnx_avatar_ai 的互斥提示
本插件与 xnx_avatar_ai 在 conf.json 的 hooks_rank 第二段路由标识都是 avatar,安装时系统会弹冲突提示。两者功能完全独立(xnx_avatar_ai 负责 AI 生成头像,本插件负责头像装饰框),可共存,忽略提示即可。
使用指南
后台管理
访问 /admin/plugin-setting-xnx_avatar_frame,包含 4 个 Tab:
Tab | 路径参数 | 用途 |
|---|---|---|
头像框列表 |
| 新建 / 编辑 / 删除头像框,上传图片,配置价格 / 有效期 / 用户组白名单 / 排序 |
购买记录 |
| 查看所有购买 / 佩戴 / 取消佩戴 / 过期日志,含用户名与花费明细 |
佩戴记录 |
| 查看当前佩戴中的头像框,含过期时间 |
全局设置 |
| 开关插件整体启用状态 |
权限:仅管理员组(gid=1,2)可访问,所有 POST 操作经 CsrfService::check() CSRF 校验。
前台页面
头像框商店:
/frame-shop.htm(分页/frame-shop-{page}.htm)—— 浏览可购买头像框,点击购买扣费我的头像框:
/frame-my.htm—— 查看已拥有头像框列表、剩余有效天数、佩戴 / 取消佩戴个人中心侧边栏:通过
my_sidebar_nav_threads_before.htm/my_sidebar_nav_threads_before_mobile.htmhook 注入「我的头像框」入口(PC + 移动端双模板)
技术细节
渲染机制
通过 avatar_component_frame.php hook 注入到核心 avatar_component_from_data() 渲染流程。hook 文件读取 $data['uid'],调用 FrameService::getWearingFrame($uid) 获取当前佩戴的头像框,再调 FrameService::renderFrameHtml() 生成 HTML 片段,以覆盖模式 $data['frame_html'] = ... 回传。核心代码零硬编码插件名,插件禁用后 hook 自动不执行,头像恢复正常显示。
框类型实现
图片框(
frame_type=0):输出<img class="avatar-frame-img">绝对定位铺满头像容器,z-index:2叠在头像本体之上CSS 框(
frame_type=1):输出<div class="avatar-frame-css">绝对定位铺满,管理员填写的frame_css作为内联 style 拼接到style属性末尾(已过滤<?php/?>等 PHP 标签字符)
积分扣费
购买流程调用 CreditsService::sub($uid, $price_type, $price_value, '购买头像框: xxx', 0, true),扣费成功后才写入拥有记录;若写入拥有记录失败,扣分不回滚(已在返回信息中提示)。CreditsService::sub 内部已包含事务,无需外层包裹。
过期双重保障
每日 cron:
model_cron_daily_end.phphook 触发FrameService::cleanExpired(),扫描所有is_wearing=1且expire_time>0的记录,过期的批量卸下并写 expire 日志显示时懒检查:
FrameService::getWearingFrame()命中过期记录时立即db_update卸下、写日志、清缓存,避免 cron 未执行时用户仍看到过期头像框
性能优化
FrameService 内置两个请求级静态缓存:
$userWearCache:uid → 佩戴头像框数据(或 null),帖子列表页同一用户多次渲染头像只查一次库$frameCache:frame_id → 头像框定义,避免购买 / 佩戴 / 日志等流程重复查 frame 表
数据表
表名 | 用途 | 关键字段 |
|---|---|---|
| 头像框定义 | id, name, frame_type, frame_image, frame_css, price_type, price_value, duration_days, allowed_gids, sort, enabled |
| 用户拥有 + 佩戴记录 | uid, frame_id, source(buy/gift/admin), cost_type, cost_value, buy_time, expire_time(0=永久), is_wearing;UNIQUE KEY (uid, frame_id) |
| 操作日志 | uid, frame_id, frame_name(冗余), action(buy/wear/unwear/expire), cost_type, cost_value, create_time |
所有表使用 utf8mb4 字符集支持 emoji。上传的头像框图片存放在 upload/avatar_frame/ 目录。
兼容性
Xiuno X 1.0+
PHP 8.x(全局数组访问已加
isset兜底)Bootstrap 5
无 jQuery 依赖(前台交互使用原生 JS)
通过
avatar_component_frame.phphook 与核心头像组件解耦,核心代码无硬编码插件名
版本历史
1.0.0:首版发布,包含头像框 CRUD、商店购买、佩戴管理、过期清理、用户组白名单、图片框 / CSS 框双类型渲染