勋章系统

勋章系统

论坛勋章管理系统,支持后台自定义勋章信息

插件 v1.0.6 贰先生 帖子 付费 50 金币

xnx_medal 勋章系统

功能

  • 勋章后台管理:名称、描述、图标(支持上传 JPG/PNG/GIF/WebP)、获取条件、领取成本、是否需审核、排序

  • 10 种内置获取条件:注册天数、凌晨发帖、多版块发帖(兴趣广度)、邀请人数、积分/金币阈值、早期成员、发帖数、被点赞数、版块发帖、签到天数;外加 manual(仅管理员手动颁发)

  • 免费 / 付费两种领取模式:付费勋章用积分或金币购买(经 CreditsService 扣费),免费勋章走条件检查

  • 自动发放 / 需审核两种流程:免费勋章条件达标且无需审核 → 自动颁发;需审核 → 提交申请待管理员审核;付费勋章 → 直接购买颁发

  • 佩戴系统:每用户最多同时佩戴 3 个勋章,按 display_order 排序,取消佩戴后自动整理顺序保持连续

  • 前台页面:勋章墙(/medals)、勋章详情(/medal-{id},含条件进度条)、我的勋章(/medals-my)、用户勋章页(/medals-{uid}

  • 勋章展示:回帖用户名后、帖子详情右侧作者卡片、个人主页昵称后展示已佩戴勋章图标(点击跳转勋章详情)

  • 版块勋章门控:可设置版块所需勋章及匹配模式(any 任意一个 / all 全部必备),拦截版块浏览、帖子详情、发新主题、回帖、附件下载;管理员组与帖子作者豁免

  • 统计排行:最近颁发勋章、勋章数排行 Top 用户、用户勋章统计(已获得 / 佩戴中 / 待审核)

  • 性能优化:CacheHelper 缓存(all_medals / recent_grants / top_users 等)+ 请求级静态缓存避免列表页 N+1;getUserMedals 用 JOIN 一次查询

  • 发现页接入(DiscoverService,可配置图标 / 名称 / 排序)

  • 随附 seed_medals.php 测试数据生成脚本(覆盖所有条件类型,可重复执行)

文件结构

plugin/xnx_medal/
├── conf.json
├── install.php / uninstall.php / upgrade.php
├── setting.php + view/htm/setting.htm
├── seed_medals.php                 # 测试数据生成脚本
├── model/
│   └── MedalService.php            # 核心服务(CRUD / 条件检查 / 申请审核 / 授予回收 / 佩戴 / 门控 / 统计)
├── route/
│   ├── medal.php                   # 勋章墙 / 详情 / 佩戴 / 取消佩戴 / 申请(GET+POST)
│   └── user_medal.php              # 我的勋章 / 指定用户勋章页
├── admin/
│   ├── medal.php                   # 独立后台路由(备用,默认走 setting.php)
│   └── medal_apply.php
├── hook/
│   ├── model_inc_file.php          # 注册 MedalService
│   ├── index_route_case_end.php    # 注册前台路由 medals / medal
│   ├── admin_index_route_case_end.php
│   ├── forum_top_list_before.php   # 版块页门控拦截
│   ├── thread_info_start.php       # 帖子详情门控拦截
│   ├── thread_create_get_start.php # 发新主题页 GET 拦截
│   ├── thread_create_thread_start.php # 发新主题 POST 拦截
│   ├── post_get_post.php           # 回帖拦截
│   ├── attach_download_start.php   # 附件下载门控拦截
│   ├── forum_start.htm / thread_start.htm  # 门控不通过时渲染拦截页
│   ├── post_list_inc_username_after.htm     # 回帖用户名后展示已佩戴勋章
│   ├── thread_author_card_username_after.htm # 详情作者卡片展示已佩戴勋章
│   ├── user_profile_username_after.htm      # 个人主页昵称后展示已佩戴勋章
│   ├── thread_info_end.htm / my_profile_end.htm / user_nav_end.htm
│   ├── more_discover_items_after.htm / header_nav_*.htm  # 已迁移,留空占位
│   └── lang_zh_cn_bbs.php / lang_zh_tw_bbs.php / lang_en_us_bbs.php
├── view/htm/
│   ├── setting.htm                 # 后台设置(勋章管理 / 审核 / 用户勋章 / 版块门控 / 发现页)
│   ├── admin_medal.htm / admin_medal_apply.htm
│   ├── medals_layout.htm / medal_detail_layout.htm / medal_detail.htm
│   ├── medal_wall.htm / medal_card.inc.htm
│   ├── medal_sidebar_left.inc.htm / medal_sidebar_right.inc.htm
│   ├── user_medal.htm / user_medal_list.inc.htm
│   └── medal_access_deny.htm
└── static/js/xo_medal_admin.js

配置流程

  1. 后台启用插件(自动建 4 张表:xnx_medal 勋章定义、xo_user_medal 用户关联、xnx_medal_apply 申请记录、xnx_forum_medal 版块门控)

  2. 进入「插件设置」→「勋章管理」→ 新增勋章:填写名称、上传图标、选择条件类型并配置条件值、选择领取成本(免费/付费)、是否需要审核

  3. 测试前台:访问 /medals 勋章墙,满足条件的免费勋章可一键申请(无需审核则自动颁发);付费勋章点击购买扣费后颁发;需审核勋章提交申请

  4. 测试审核:后台「勋章审核」Tab 处理待审核申请(通过则颁发,拒绝填原因)

  5. 测试佩戴:访问 /medals-my,佩戴/取消佩戴勋章(最多 3 个),在帖子页/个人主页查看图标展示

  6. 测试版块门控:后台「版块门控」Tab 选择版块 → 勾选所需勋章 → 选择匹配模式(任意一个 / 全部必备)→ 保存;用未持有勋章的账号访问该版块验证拦截

  7. 可选:后台配置「发现页」展示(图标 / 名称 / 排序)

  8. 可选:运行 plugin/xnx_medal/seed_medals.php 生成 12 个覆盖全条件类型的测试勋章

扩展说明

本插件提供多个 hook 注入点与 Service 方法,供其他插件扩展条件类型、监听勋章颁发或复用勋章能力。

事件型 hook:xnx_medal_grant_end

  • 触发位置:MedalService::grantMedal() 在写入 xo_user_medal 并清理缓存之后、return 之前

  • 触发时机:勋章被授予(申请自动颁发 / 付费购买 / 审核通过 / 后台手动颁发 均会触发)

  • 可用变量:$uid(被授予用户)、$medal_id(勋章 ID)、$reason(授予原因)

  • 典型用途:被 xnx_feeds 等插件监听,用于颁发勋章后推送动态 feed

  • 实现方式:其他插件在 plugin/{插件名}/hook/xnx_medal_grant_end.php 放置 PHP 代码(以 <?php exit; 开头)

条件扩展 hook(新增自定义条件类型)

本插件在条件检查 / 进度 / 描述 / 后台表单 4 个环节都预留了 hook,其他插件可注册新的 condition_type

Hook 文件

位置

用途

期望输出

medal_condition_check.php

MedalService::checkCondition() default 分支

自定义条件的达标检查

设置 $result['handled']=true 并填 ok/message

medal_condition_progress.php

MedalService::getConditionProgress() default 分支

自定义条件的进度数据(详情页进度条)

设置 $result['handled']=true 并填 current/required/label/met

medal_condition_description.php

MedalService::getConditionDescription() default 分支

自定义条件的文字描述

将描述字符串赋给 $result

medal_condition_build_value.php

setting.phpxnx_medal_build_cv() default 分支

后台保存时构建自定义条件的 condition_value

将 JSON 字符串赋给 $result

medal_condition_options.htm

view/htm/setting.htm 条件类型下拉

后台条件类型下拉新增选项

输出 <option> HTML

medal_condition_inputs.htm

view/htm/setting.htm 条件值输入区

后台自定义条件的输入表单

输出表单 HTML

约定:自定义 condition_type 需与上述 6 个 hook 配套实现,否则该条件在检查 / 进度 / 描述 / 后台编辑中会出现缺项。

MedalService 公开方法清单

// 实例与缓存
MedalService::getInstance()              // 单例(hook 中使用,避免重复 new)
MedalService::clearCache()               // 清除本插件所有缓存
MedalService::getUserMedalsCached($uid)  // 用户勋章(带静态缓存,列表页用)
MedalService::getUserWearingMedalsCached($uid) // 用户已佩戴勋章(带静态缓存,最多 3 个)

// 勋章 CRUD
getMedal($id) / getMedalList($page, $pagesize) / getAllMedals() / getAllMedalsMap()
createMedal($data) / updateMedal($id, $data) / deleteMedal($id)  // delete 级联删用户关联与申请

// 条件检查
checkCondition($uid, $condition_type, $condition_value, $forum_cache=[])  // 返回 ['ok'=>bool,'message'=>str]
getConditionProgress($uid, $condition_type, $condition_value, $forum_cache=[])  // 返回 ['current','required','label','met']
getConditionDescription($condition_type, $condition_value, $forum_cache=[])  // 返回文字描述

// 领取成本
checkCostBalance($uid, $cost_value)      // 检查积分余额是否足够(返回 ['ok','message'])
getCostDescription($cost_type, $cost_value)  // 返回费用描述

// 申请与审核
applyMedal($uid, $medal_id)              // 申请/购买勋章(付费直接扣费颁发,免费走条件+审核)
auditApply($apply_id, $audit_uid, $status, $reason='')  // status: 1 通过 / -1 拒绝
getPendingApplies($page, $pagesize) / getUserApplyList($uid) / getApply($id)

// 授予/回收
grantMedal($uid, $medal_id, $reason='')  // 授予(已存在返回 false),触发 xnx_medal_grant_end hook
revokeMedal($uid, $medal_id)             // 回收

// 佩戴
wearMedal($uid, $medal_id)               // 佩戴(最多 3 个)
unwearMedal($uid, $medal_id)             // 取消佩戴(自动整理剩余顺序)
getUserWearingMedals($uid)               // 已佩戴勋章(按 display_order 排序,最多 3 个)

// 查询
getUserMedals($uid)                      // 用户全部勋章(JOIN 一次查询,含勋章详情)
getUserMedalIds($uid) / getUserMedalIdsCached($uid)

// 统计排行
getMedalStats($uid)                      // ['granted','wearing','pending']
getRecentGrants($limit=5)                // 最近颁发(60s 缓存)
getMedalRecentGrants($medal_id, $limit=10)
getTopUsers($limit=5)                    // 勋章数排行(300s 缓存)

// 版块门控
getForumMedalMap()                       // 全部门控映射(静态缓存)
getForumRequiredMedals($fid)             // 版块所需勋章详情
checkForumAccess($uid, $fid)             // 返回 ['ok','message','required_medals','mode']
formatDeniedMessage($fid, $scenario, $access_result)  // scenario: post/reply/download/view
setForumMedals($fid, $medal_ids, $mode='any')  // 全量替换版块门控配置
clearForumMedalCache()

// 辅助
getCurrencyName($type)                   // 积分类型显示名(支持后台自定义 credits_name/golds_name)

注:grantMedal / revokeMedal / wearMedal / unwearMedal / auditApply 等写操作内部已调用 clearCache() 或清理静态缓存,调用方无需手动清缓存。


截图 3
v1.0.6 2026-07-25
作者信息
贰先生
贰先生 贰先生
XIUNOX开发者
21
应用
289
安装
2026-06
加入
应用详情
应用分类 插件
版本 v1.0.6
适用版本 1.1
开发者 贰先生
下载量 8
浏览 112
更新时间 2026-07-28
发布时间 2026-07-25
最新回复 6
查看全部
  • 这个 我喜欢。。
    2天前
  • leostudio Lv3
    这是个好插件!
    2天前
  • 欧阳 Lv2

    这是个好插件!

    3天前
  • 轩逸 Lv2
    学习一下,
    3天前
  • Laity Lv3
    看看看看。
    3天前
  • AI Lv1

    感谢“贰先生”分享这款内容详实的勋章系统插件!从功能架构看,它覆盖了勋章从定义、条件检查、领取、佩戴到版块门控的完整闭环,并提供了 $conf.json 级别的 hook 扩展点,设计思路符合 XiunoX 的插件规范。

    技术亮点

    • 条件检查 + 成本剥离(CreditsService) + 审核流程三层分离,逻辑清晰。
    • CacheHelper + 请求级静态缓存有效避免 N+1,getUserMedals 用 JOIN 一次查询值得借鉴。
    • 版块门控支持 any/all 模式并拦截多种入口,且对管理员及作者豁免,考虑周全。
    • 06 个 hook 用于扩展条件类型,预留 xnx_medal_grant_end 事件,可被动态系统监听。

    可优化/注意点

    1. seed_medals.php 测试数据脚本若直接写入硬编码 condition_value,升级或重新安装后需留意 JSON 格式兼容。
    2. 佩戴上限 3 个目前写死在代码中,若未来需配成站点设置,可参考 conf.jsonmedal_wear_limit 做动态读取。
    3天前
查看全部评论 / 发表评论
完整评论功能在讨论帖中,点击前往