找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 19|回复: 0

[维护日志] [2026-07-02] [功能] [新手引导流程] - 论坛新手任务与打卡引导流程改造

[复制链接]
  • 打卡等级:即来则安

7

威望

409

金钱

17

贡献

超级版主

积分
499
主题
9
回帖
40
注册时间
2004-3-7
最后登录
2026-9-6
发表于 2026-7-3 00:59:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

1、问题缘起及探讨过程中提出的所有新问题汇总

原始需求: 在 Discuz 论坛中实现一套完整的新手引导流程,确保新注册用户依次完成"打卡签到"和"新人报到回帖"两个任务后才能正常使用论坛。

任务 ID 从 10 变更为 14,是因为论坛后台重新创建了“新人报到”任务(原任务可能被删除或停用)。

探讨过程中提出的独立问题:

序号问题描述状态
1新手任务弹窗遮挡 Discuz 本身的头像设置提示页面已解决
2发帖后任务状态未更新,仍被强制跳转回目标帖子已解决
3任务 ID 从 10 变更为 14,需同步更新代码已解决
4Discuz 原生任务插件 task_post 的 threadid 功能失效(与 forumid 优先级冲突)已解决
5注册完成后直接跳转打卡页面,缺少中间引导弹窗已解决
6打卡成功后无弹窗直接跳转任务页,用户体验断裂已解决
7老用户自动打卡出现 showWindow loading 转圈卡住已解决
8打卡页面已打卡后未触发跳转,用户被留在打卡页已解决





2、解决方案

(1)简要技术分析

通过 Discuz 插件钩子 global_footer 拦截页面输出,根据用户组(groupid=10 新手)、打卡状态、任务状态三层条件分支,动态注入 showDialog 弹窗和 window.location.href 跳转脚本,实现新手引导流程的闭环控制;同时修复原生任务插件 task_post 的 csc() 和 view() 方法中 threadid 与 forumid 的优先级冲突。

(2)修改、新增文件列表

文件路径操作
source/plugin/zqlj_sign/hook.class.php修改(核心逻辑)
source/class/task/task_post.php修改(任务插件)
source/class/task/lang/lang_post.php修改(语言包)


(3)关键作用代码段全文

文件一:source/plugin/zqlj_sign/hook.class.php — 核心主控逻辑

  1. function _ajax_and_go(){
  2.     global $_G,$_sign_init;
  3.     if($_G['uid']){
  4.         $ajax=$gojs='';
  5.         $newbie_redirect = '';
  6.         $sign_completed = false;
  7.         
  8.         // ============================================================
  9.         // 第一步:自动打卡(区分新手/老用户)
  10.         // ============================================================
  11.         if(in_array($_G['groupid'],$_sign_init['groups'])){
  12.             if($_sign_init['open']&&!zqlj_sign_status($_G['uid'])){
  13.                 if(in_array($_G['groupid'],$_sign_init['ajaxgroups'])){
  14.                     if($_sign_init['mobile']){
  15.                         $ajax='<!-- auto sign -->';
  16.                         $result=zqlj_sign($_G['uid']);
  17.                         if($result==2){
  18.                             if($_G['groupid'] == 10){
  19.                                 // 新手:打卡后引导去任务页
  20.                                 $sign_completed = true;
  21.                                 $ajax.='<script>
  22.                                     showDialog(\'打卡成功!接下来去完成新手任务吧。\', \'notice\', \'提示\', function(){
  23.                                         window.location.href="' . $_G['siteurl'] . 'home.php?mod=task";
  24.                                     });
  25.                                 </script>';
  26.                             } else {
  27.                                 // 老用户:静默打卡
  28.                                 $ajax.='<script>showDialog(\'打卡成功!\', \'notice\', \'提示\');</script>';
  29.                             }
  30.                         }                                       
  31.                     }else{        
  32.                         if($_G['groupid'] == 10){
  33.                             // 新手:打卡窗口带回调跳转
  34.                             $ajax=zqlj_sign_url('<script type="text/javascript">
  35.                                 showWindow(\'zqlj_sign\', \'plugin.php?id=zqlj_sign&sign='.$_sign_init['formhash'].'&ajax=yes\', function(){
  36.                                     showDialog(\'打卡成功!接下来去完成新手任务吧。\', \'notice\', \'提示\', function(){
  37.                                         window.location.href="' . $_G['siteurl'] . 'home.php?mod=task";
  38.                                     });
  39.                                 });
  40.                             </script>');
  41.                         } else {
  42.                             // 老用户:打卡窗口无回调,避免loading卡住
  43.                             $ajax=zqlj_sign_url('<script type="text/javascript">
  44.                                 showWindow(\'zqlj_sign\', \'plugin.php?id=zqlj_sign&sign='.$_sign_init['formhash'].'&ajax=yes\');
  45.                             </script>');
  46.                         }
  47.                     }
  48.                 }
  49.                 if(!$ajax&&in_array($_G['groupid'],$_sign_init['gogroups'])&&$_GET['id']!='zqlj_sign'){
  50.                     if($_G['groupid'] == 10){
  51.                         // 新手:中间引导弹窗
  52.                         $gojs='<script>
  53.                             showDialog(\'接下来花几分钟时间完成两个新手任务,有积分拿哦!<br><br>在此之前,先一键打卡!\', \'notice\', \'新手引导\', function(){
  54.                                 window.location.href=\'' . $_G['siteurl'] . 'plugin.php?id=zqlj_sign\';
  55.                             });
  56.                         </script>';
  57.                     } else {
  58.                         // 老用户:直接跳转
  59.                         $gojs=zqlj_sign_url('<script>window.location.href=\'plugin.php?id=zqlj_sign\';</script>');
  60.                     }
  61.                 }
  62.                 if(CURSCRIPT=='home'&&CURMODULE=='spacecp'&&($_GET['ac']=='profile'||$_GET['ac']=='avatar')) {
  63.                     $ajax='';
  64.                     $gojs='';
  65.                     $sign_completed = false;
  66.                 }                                
  67.             }
  68.         }
  69.         
  70.         // ============================================================
  71.         // 第二步:检查任务 14 状态(新人报到任务)
  72.         // ============================================================
  73.         $task_10_status = false;
  74.         $task_10_completed = false;
  75.         if ($_G['uid']) {
  76.             $task_info = DB::fetch_first("SELECT status FROM ".DB::table('common_mytask')." WHERE uid='$_G[uid]' AND taskid=14");
  77.             if ($task_info) {
  78.                 $task_10_status = true;
  79.                 if ($task_info['status'] == 1 || $task_info['status'] == 2) {
  80.                     $task_10_completed = true;
  81.                 }
  82.             }
  83.         }
  84.         
  85.         // ============================================================
  86.         // 第三步:根据任务状态决定跳转
  87.         // ============================================================
  88.         if(empty($gojs) && !$sign_completed){
  89.             
  90.             // 情况1:任务已完成 → 放行
  91.             if ($task_10_completed) {
  92.                 // 放行
  93.             }
  94.             
  95.             // 情况2:已领取但未完成 → 跳转到目标帖子
  96.             else if ($task_10_status && getuserprofile('posts') == 0 && strpos($_SERVER['REQUEST_URI'], 'thread-95959-1-1.html') === false) {
  97.                 $is_fastpost = (CURSCRIPT=='forum' && $_GET['mod']=='post' && $_GET['action']=='reply' && $_GET['replysubmit']=='yes' && $_GET['infloat']=='yes' && $_GET['handlekey']=='fastpost');
  98.                 if (strpos($_SERVER['REQUEST_URI'], 'thread-95959-1-1.html') === false &&
  99.                     !(CURSCRIPT=='home'&&CURMODULE=='spacecp'&&in_array($_GET['ac'], array('profile', 'avatar'))) &&
  100.                     !$is_fastpost) {
  101.                     $newbie_redirect = '<script>
  102.                         showDialog(\'您已领取新手任务,请前往新人报到帖完成回帖,才能正式成为会员。\', \'notice\', \'提示\', function(){
  103.                             window.location.href="' . $_G['siteurl'] . 'thread-95959-1-1.html";
  104.                         });
  105.                     </script>';
  106.                 }
  107.             }
  108.             
  109.             // 情况3:未领取任务 → 跳转到任务列表
  110.             else if ($_G['groupid'] == 10 && getuserprofile('posts') == 0 &&
  111.                      strpos($_SERVER['REQUEST_URI'], 'home.php?mod=task') === false &&
  112.                      strpos($_SERVER['REQUEST_URI'], 'thread-95959-1-1.html') === false) {
  113.                 $is_fastpost_float = (CURSCRIPT=='forum' && $_GET['mod']=='post' && $_GET['action']=='reply' && $_GET['replysubmit']=='yes' && $_GET['infloat']=='yes' && $_GET['handlekey']=='fastpost');
  114.                 $is_signed = zqlj_sign_status($_G['uid']);
  115.                 if (!(CURSCRIPT=='home'&&CURMODULE=='spacecp'&&in_array($_GET['ac'], array('profile', 'avatar'))) && !$is_fastpost_float && ($_GET['id'] != 'zqlj_sign' || $is_signed)) {
  116.                     $newbie_redirect = '<script>
  117.                         showDialog(\'接下来去完成新手任务吧。\', \'notice\', \'提示\', function(){
  118.                             window.location.href="' . $_G['siteurl'] . 'home.php?mod=task";
  119.                         });
  120.                     </script>';
  121.                 }
  122.             }
  123.         }
  124.         
  125.         return $ajax.$gojs.$newbie_redirect;
  126.     }
  127.     return '';
  128. }
复制代码


文件二:source/class/task/task_post.php — csc() 方法修复(调整 threadid 优先级)

  1.                 $tbladd = $sqladd = '';
  2.                 if($taskvars['act'] == 'newreply' && $taskvars['threadid']) {
  3.                         $threadid = intval($taskvars['threadid']);
  4.                 }
  5.                 if($taskvars['forumid']) {
  6.                         $forumid = intval($taskvars['forumid']);
  7.                 }
  8.                 if($taskvars['author']) {
  9.                         return TRUE;
  10.                 }
复制代码


文件三:source/class/task/task_post.php — view() 方法修复(调换 threadid 与 forumid 优先级)

  1.         function view($task, $taskvars) {
  2.                 global $_G;
  3.                 $return = $value = '';
  4.                 if(!empty($taskvars['complete']['threadid'])) {
  5.                         $value = intval($taskvars['complete']['threadid']['value']);
  6.                         $thread = C::t('forum_thread')->fetch_thread($value);
  7.                         $value = '<a href="forum.php?mod=viewthread&tid='.$value.'"><strong>'.($thread['subject'] ? $thread['subject'] : 'TID '.$value).'</strong></a>';
  8.                 } elseif(!empty($taskvars['complete']['forumid'])) {
  9.                         $value = intval($taskvars['complete']['forumid']['value']);
  10.                         loadcache('forums');
  11.                         $value = '<a href="forum.php?mod=forumdisplay&fid='.$value.'"><strong>'.$_G['cache']['forums'][$value]['name'].'</strong></a>';
  12.                 } elseif(!empty($taskvars['complete']['author'])) {
复制代码


文件四:source/class/task/lang/lang_post.php — 语言包修改(明确引导目标)

  1. 'task_complete_act_newreply_thread' => '在主题帖"{value}"中回复 {num} 次',
复制代码

修改语言包的目的:将“新人报到”任务的完成提示从泛泛的“在指定版块回复”改为包含具体帖子(thread-95959-1-1.html)的引导,使新手用户明确知道需要去哪个帖子回帖。




3、最终实现效果

用户类型访问场景行为
新注册用户(groupid=10)首次访问论坛弹窗"接下来花几分钟时间完成两个新手任务..." → 点击跳转打卡页面
打卡页面手动打卡弹窗"打卡成功!接下来去完成新手任务吧" → 点击跳转任务列表
任务列表领取任务后访问其他页面弹窗"您已领取新手任务,请前往新人报到帖..." → 点击跳转目标帖子
目标帖子回帖完成任务完成,后续访问放行
资料编辑/头像上传页放行,不拦截
快速回复浮层页放行,不遮挡 Discuz 原生提示
老用户(groupid≠10)访问论坛自动打卡浮层,打卡成功显示"打卡成功!",无强制跳转
打卡页面正常打卡,无新手引导弹窗


引导链流程图:

  1. 新用户注册
  2.     │
  3.     ├── 引导弹窗 → 打卡页面
  4.     │
  5.     ├── 打卡成功 → 任务列表页
  6.     │
  7.     ├── 领取任务14 → 目标帖子(thread-95959-1-1.html)
  8.     │
  9.     └── 完成回帖 → 任务完成 ✅ → 放行
复制代码





4、重要技术沉淀 / 踩坑点

踩坑点正确做法
showWindow 回调函数在窗口打开时即执行,而非打卡成功后老用户去掉回调,避免 loading 转圈卡住
task_post 的 view() 中 forumid 优先级高于 threadid,同时设置时后者被跳过调换判断顺序,让 threadid 优先匹配
新手引导弹窗遮挡头像设置、快速回复等 Discuz 原生提示页面在放行条件中增加 profile、avatar、fastpost 白名单
打卡页面已打卡后用户被留在打卡页,无法跳转情况3去掉 $_GET['id'] != 'zqlj_sign' 硬排除,改为 $_GET['id'] != 'zqlj_sign'$is_signed
任务 ID 硬编码在代码中,变更时需要同步修改当前任务 ID 14 和目标帖子 ID 95959 已硬编码,后续可考虑后台配置化


涉及插件版本: 天天打卡签到 v7.5.0(已修改,无官方更新)




5、进一步优化可能性

  • 当前任务 ID(14)和目标帖子 ID(95959)硬编码在 hook 中,未来可考虑后台配置化。
  • 新手引导流程可扩展为更多步骤(如完善资料、上传头像),当前仅覆盖打卡+回帖两步。
  • 可考虑使用 Cookie 或 Session 记录引导进度,避免用户刷新页面重复弹窗。

要养成随时修正错别字的好习惯!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|点拨论坛 |网站地图

GMT+8, 2026-10-2 02:10 , Processed in 0.313226 second(s), 7 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表