[开发]每日签到(daily_checkin)插件说明

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

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/plugins/daily_checkin/说明.md。

每日签到(daily_checkin)插件说明

版本:1.3.0 | 作者:FlintHub | 状态:✅ 启用(plugin.json activated: true)

功能:用户每日签到获取积分,支持连续签到奖励(连签天数加成);后台可配置开关与奖励参数


一、功能简介

  • 用户每日可签到一次,签到获得基础积分(checkin_points_base,默认 2)
  • 连续签到奖励:连续签到天数加成(checkin_points_bonus,默认 1/天)
  • 数据表 UNIQUE(user_id, checkin_date) 天然去重,一天只能签一次
  • 签到总开关(checkin_enabled,默认开):关闭后右栏不出面板、签到按钮置灰、直 POST 被拒
  • 两个前台入口(2026-10-03 起:已按要求移除「顶部导航」入口):
  1. 右栏「本周签到」面板(right_sidebar_after 钩子,用户栏下方、与用户栏同级)
  2. 前台导航「应用」下拉(nav_plugin_links)
  • 一个后台入口:/admin/daily-checkin 设置页(开关 + 奖励参数 + 只读统计)
  • 签到页 /checkin:积分概览 + 奖励规则 + 本月日历(可翻月)

二、目录结构

plugins/daily_checkin/
├── plugin.json              # 插件清单(version 1.3.0 / author FlintHub / admin_url / permissions)
├── Plugin.php               # 生命周期(activate 建表 + 默认设置)+ 设置读写(getSettings/saveSettings)+ class_alias
├── CheckinController.php    # 前台签到控制器(index / buildAreaData / doCheckin)
├── AdminController.php      # ★ 后台控制器(index / stats / settingsSave),继承 Admin\BaseController
├── hook/
│   ├── init_after.php              # 懒建表 + checkin_date 字段类型迁移
│   ├── route_register.php          # 前台路由注册(/checkin)
│   ├── admin_route_register.php    # ★ 后台路由注册(/daily-checkin、/daily-checkin/settings/save)
│   ├── right_sidebar_after.php     # ★ 右栏「本周签到」面板(自取数据,渲染 _sidebar.php)
│   ├── nav_plugin_links.php        # 前台「应用」下拉入口
│   └── nav_user_menu_items.php     # 用户下拉菜单入口(当前为空实现,作者禁用)
├── assets/
│   ├── style.css            # 前台样式(dc-modern-* 签到页 / dc-week-* 右栏面板)
│   └── admin.css            # ★ 后台设置页样式(dc-admin-*)
├── lang/
│   ├── zh.php / en.php / zh_tw.php  # 三语字典(各 42 键,键集与键序一致)
└── views/
    ├── index.php            # 签到页面外壳
    ├── _area.php            # 签到页主体(积分概览 + 奖励规则 + 本月日历)
    ├── _sidebar.php         # ★ 右栏「本周签到」面板(钩子 + htmx 回包共用)
    └── admin.php            # ★ 后台设置页

三、数据表

表用途
daily_checkin签到记录(user_id / checkin_date / points / consecutive_days / created_at,UNIQUE(user_id, checkin_date))

  • 插件独立库:plugins/daily_checkin/data/daily_checkin.sqlite(Plugin::db() → \app\Helpers\Plugin::db('daily_checkin'))
  • activate() 建表 + 写入默认奖励配置(settings 表,见下)
  • uninstall() 删除表
  • 卸载后 users.points 字段保留(不删除,防误删其他积分逻辑)

设置项(存核心 settings 表,group = daily_checkin)

键默认说明
checkin_enabled1是否开放签到(1 开 / 0 关)
checkin_points_base2每日签到基础积分
checkin_points_bonus1连续签到每天的额外加成

⚠️ 写库必须走 Settings::update($key,$value,'daily_checkin'),不要用 Model\Setting::setValue()。

Settings::get() 会优先命中 protected/settings_cache.php 缓存文件,而 Settings::update()

内部才会 buildCache() 重建缓存 → 只写库不清缓存 = 「后台显示保存成功、前台读到的仍是旧值」,零日志。

四、Hooks

Hook用途
init_after懒建表 + 迁移
route_register前台路由(/checkin)
admin_route_register后台路由(/daily-checkin、/daily-checkin/settings/save)
right_sidebar_after右栏「本周签到」面板(用户栏下方、与用户栏同级)
nav_plugin_links「应用」下拉签到入口
nav_user_menu_items用户菜单入口(当前为空实现,作者禁用)

⚠️ nav_main_links(顶部导航入口)已于 2026-10-03 按要求移除:

钩子注册从 plugin.json 的 hooks 中删除,hook/nav_main_links.php 文件同步删除。

签到入口保留在右栏面板与「应用」下拉两处(PC 左栏「应用」面板 + 顶栏「应用」下拉共用同一钩子)。

⚠️ 若要恢复顶部导航入口:重新加回 plugin.json 的 nav_main_links 键 + 重建该钩子文件即可。

改 plugin.json 的 hooks 后必须刷新 plugins/plugins_cache.json(删缓存后触发一次前台请求即可重建)。

⚠️ 验证时注意:无 Cookie 的纯游客 GET 会命中 pages/ 页面缓存直接退出、完全跳过 init.php

→ 插件缓存永不重建。要触发重建必须带 Cookie。

⚠️ 已移除 controller_view_before:它原先把 checkin_today / checkin_today_points /

checkin_consecutive / checkin_points 注入视图,而唯一的消费方是核心右栏里那段硬编码的签到代码

(right_sidebar.php 的 include('plugins/daily_checkin/_sidebar') + #checkinHint)。

2026-09-27 核心完成解耦、那段代码被删除后,该注入再无消费方,故连同钩子一并移除。

现在面板由 right_sidebar_after 自己查库(每次渲染 1 次查询;右栏被隐藏时零查询)。

五、后台设置页(/admin/daily-checkin)

  • 控制器 AdminController 继承 app\Controllers\Admin\BaseController —— 其构造函数已做

Auth::isAdmin() 门禁(AND 口径:role === 'admin' 且 group_id === 4)与管理员二次验证。

⚠️ 不要改成继承 app\Core\Controller —— 会绕过全部后台门禁(越权高危,本仓已复发 2 次)。

  • 页面内容:
  1. 只读统计三格:今日签到人数 / 本周参与人数 / 累计签到人次(stats() 三条 COUNT,异常吞掉返回 0,不阻断设置页)
  2. 设置表单:签到开关(checkbox)+ 基础积分 + 连签加成(number,0~1000 防手滑),提交后 302 回本页
  • 表单两个隐性契约:
  • 哨兵字段 settings_submitted:checkbox 未勾选时浏览器根本不提交该字段,靠哨兵区分

「提交过但没勾选」与「压根没提交表单」两种情况。

  • 逐字段 array_key_exists 判定,⚠️ 禁止写 (int)($_POST[$k] ?? 0):字段缺失会被静默当成

「设为 0」写库(症状:302 显示保存成功,但 DB 与缓存都不变、零日志)。

  • 视图红线:全部 markup 必须留在 <main class="admin-content"> 内 —— 核心后台表单/按钮样式

全是 .admin-content 作用域(admin.css:237-282),放到 </main> 之后 = 「弹出页没有 UI」,零日志。

  • CSS 版本号:views/admin.php 的 section('css') 里用 __DIR__ . '/../assets/admin.css' +

substr(md5_file($css),0,8) 自动破缓存。⚠️ 视图目录是 views/,样式在同级 ../assets/;

写成 ../../assets/ 恒不命中 → 版本号永远退化成 v=1,改了样式不破缓存。

  • 写操作审计:保存成功记 AuditLog::log('daily_checkin_settings_save', 'dc_settings', 0, 'keys=...')。
  • 参数白名单:Plugin::saveSettings() 只接受 SETTING_DEFAULTS 里有的键,白名单外的键一律忽略

(防构造 POST 覆写任意核心 settings 键)。

六、右栏「本周签到」面板(right_sidebar_after)

  • 位置:app/Views/_components/right_sidebar.php:210 —— 用户面板之后、站点统计之前
  • 形态:独立 .mn-panel,与排行榜 / 最新会员 / 站点统计一致
  • 内容:标题(带跳转 /checkin 的右箭头)+ 本周 7 格(周一 → 周日)+ 状态区 + 底部提示
  • 已签到的日子:主色实心圆 + 勾
  • 今日未签到:立即签到 按钮(htmx hx-post="/checkin",hx-target="#checkinWeek" 原地替换)
  • 今日已签到:绿胶囊「今日已签到」
  • 底部灰字:未签到显示副标题;已签到显示「明天见」
  • 只对登录用户显示;游客直接 return,不输出任何内容
  • 签到开关关闭时不出面板:钩子里读 \app\Helpers\Settings::get('checkin_enabled','1') 判断,

非 '1' 即 return。⚠️ 不要在钩子里调 Plugin::isEnabled() —— 渲染钩子在静态闭包里执行,

加载顺序不保证插件主类已就位;直接读 Settings 更稳。

  • 数据:SELECT checkin_date, points, consecutive_days ... WHERE checkin_date BETWEEN 本周一 AND 本周日

(1 次查询同时得出 7 格状态 + 今日积分/连续天数)

  • 图标:一律 <i class="fa mn-fs-12">,不用 $view->icon()(后者带内联 style,违反插件零内联)
  • 渲染钩子红线:闭包作用域内 $this 不可用 → 钩子里用 htmlspecialchars() / Csrf::token()

⚠️ 已知差异:两处日历的「周起始日」不同

位置起始日表头顺序
右栏「本周签到」面板(_sidebar.php)周一一 二 三 四 五 六 日
签到页「本月日历」(_area.php)周日日 一 二 三 四 五 六

右栏面板对齐参考设计(M T W T F S S);_area.php 的月份日历未改动。

两处刻意保留各自口径,不是 bug,改动前请先确认。

⚠️ 模板取数红线

_sidebar.php 有两个入口,取数方式必须统一为普通变量($week_days / $checkin_today):

入口路径`$this->getData()`普通变量
右栏钩子$GLOBALS['__view']->include($tpl, $extra)❌ 取不到✅
htmx 回包viewRaw($tpl, $data) → display() 会 extract✅✅

原因:TemplateCompiler::include($tpl, $extra) 是 array_merge($this->data, $extra) 后 extract,

不会把 $extra 写回 $this->data → 钩子路径下 getData() 拿到的是默认值。

所以模板里禁止 $this->getData()。($this->e() / $this->url() / $this->t() 两条路径都可用。)

七、路由

前台

方法路径说明
GET/checkin签到页面(积分概览 + 奖励规则 + 本月日历,支持 ?month=YYYY-MM 翻月)
POST/checkin执行签到。htmx 请求(HX-Request)返回局部 HTML;普通表单提交 302 回 /checkin

后台(admin_route_register)

方法路径说明
GET/admin/daily-checkin后台设置页(统计 + 表单)
POST/admin/daily-checkin/settings/save保存设置(CSRF + 白名单 + 302 PRG)

⚠️ hook/admin_route_register.php 里注册的路径不写 /admin 前缀(核心已在 admin 分组内注册,

再写会 404 —— 规范 §11.22)。plugin.json 的 admin_url = /admin/daily-checkin(含前缀),两边拼起来才是最终 URL。

八、核心逻辑(doCheckin)

  1. 签到开关检查:Plugin::isEnabled() 为假 → 写入 $_SESSION['checkin_msg'] = msg_disabled,

htmx 请求返回局部 _area 片段(exit),普通表单 302 回 /checkin

  1. 校验登录 + CSRF
  2. 查今日是否已签(UNIQUE(user_id, checkin_date) + 前置查询)→ 已签返回提示
  3. 计算连续天数:昨日有签 → 连续 +1;否则重置为 1
  4. 写入签到记录 → Points::award 发放积分(基础 + 连签加成)
  5. htmx:viewRaw() 重渲染(widget=sidebar → _sidebar.php,否则 _area.php);普通表单:302 回 /checkin

buildAreaData() 里 $data['checkin_enabled'] = Plugin::isEnabled(); 必须在 try 之外赋值

(异常分支也要带上该字段,否则 _area.php 取到默认值 true,关站时按钮仍可点)。

九、注意事项

  • 签到记录按 checkin_date TEXT(Y-m-d)去重,跨时区以服务器日期为准
  • 连续天数判断依赖昨日记录,跨天未签则断签重置
  • 入口 nav_user_menu_items 文件当前为空实现(作者禁用),实际入口走顶部导航 + 「应用」下拉 + 右栏面板
  • ⚠️ 目录名含下划线(daily_checkin),核心用目录名硬拼类名 \Plugin\{目录名}\Plugin

(app/Helpers/Plugin.php:699/750/846),而本插件实际命名空间是 Plugin\DailyCheckin

→ class_exists() 恒为 false,activate() / deactivate() / uninstall() 会被静默跳过。

✅ 已修(v1.1.0):Plugin.php 末尾加了

\class_alias(Plugin::class, 'Plugin\\daily_checkin\\Plugin', false);

→ class_exists('\Plugin\daily_checkin\Plugin') 实测为 true,卸载可正常删表。

该行不可删除:删掉后生命周期再次失效,且只在卸载时才暴露,极难排查。

  • ⚠️ 改插件静态资源(css/js)后必须 bump 版本号 —— 后台 admin.css 走 md5_file 自动破缓存;

前台 style.css 若为硬编码 ?v=,需手工同步。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 5 次 | 回覆 1 次

全部回覆 (1)

🌳Lv.4 中级 🌛 见习会员
2026-10-26 01:50:52
1.3.0 这个版本补的三条都是实用项:UNIQUE(user_id, checkin_date) 天然去重、总开关关闭后「右栏不出面板 + 按钮置灰 + 直 POST 被拒」三层联动、连签加成可配。

三点:

1. checkin_date 按服务器时区还是用户时区?跨时区用户会在凌晨出现「今天签过了 / 昨天算断签」的错位,至少在页面上写明「按北京时间」。
2. 连签加成默认 1 分/天、没有封顶,连签 200 天就是 202 分/天。建议给加成设个上限(比如 30 分),否则长期用户的积分会压过整个经济池。
3. 下午再说一句:这个标题和之前 09-27 那份完全一样,旧版已按你的要求删掉了,以后同类文档建议在标题里保留版本日期,避免两篇同名帖并列在列表里。
#1 樓

請 登入