### 变更详情 1. 新增密保问题系统,支持8种预置验证问题,多场景支持多验证方式 2. 新增勋章管理模块,包含勋章配置、用户勋章关联管理 3. 新增每日任务系统,支持任务配置和用户进度追踪 4. 新增赛季排行榜功能,支持周/月赛季排行与奖励结算 5. 新增信息流推荐权重配置管理 6. 重构服务路径分层,按设备/网络/数据分类管理服务 7. 优化Feed请求参数截断逻辑,避免URL过长 8. 新增等级工具类,统一处理等级颜色与称号展示 9. 新增屏幕共享共享信令Provider,复用传输服务实例 10. 新增Android/iOS分享适配与桌面小组件支持 11. 清理旧版测试脚本,新增部署维护脚本 12. 完善用户注销关联数据清理逻辑
388 lines
13 KiB
PHP
388 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* @name 每日任务API接口
|
|
* @author AI Coder
|
|
* @date 2026-05-14
|
|
* @desc 每日任务系统: 今日任务列表/上报进度/领取奖励/完美日/注册自定义任务
|
|
* @update v12.0.0 初始版本
|
|
*/
|
|
|
|
namespace app\api\controller;
|
|
|
|
use app\common\controller\Api;
|
|
use think\Db;
|
|
|
|
class Task extends Api
|
|
{
|
|
protected $noNeedLogin = [];
|
|
protected $noNeedRight = ['*'];
|
|
|
|
protected $rateLimit = [
|
|
'reportProgress' => ['limit' => 30, 'window' => 60],
|
|
'claim' => ['limit' => 20, 'window' => 60],
|
|
'claimPerfect' => ['limit' => 10, 'window' => 60],
|
|
'registerCustom' => ['limit' => 5, 'window' => 3600],
|
|
];
|
|
|
|
public function _initialize()
|
|
{
|
|
if (isset($_SERVER['HTTP_ORIGIN'])) {
|
|
header('Access-Control-Expose-Headers: __token__');
|
|
}
|
|
check_cors_request();
|
|
parent::_initialize();
|
|
}
|
|
|
|
/**
|
|
* @name 今日任务列表
|
|
* @desc 获取今日任务列表(含进度),固定任务+随机抽取3-4个随机任务
|
|
*/
|
|
public function today()
|
|
{
|
|
$userId = $this->auth->id;
|
|
if (!$userId) $this->error('请先登录');
|
|
|
|
$today = date('Y-m-d');
|
|
$fixedTasks = Db::name('daily_task')
|
|
->where('status', 'normal')
|
|
->where('is_random', 0)
|
|
->order('weigh desc, id asc')
|
|
->select();
|
|
|
|
$randomPool = Db::name('daily_task')
|
|
->where('status', 'normal')
|
|
->where('is_random', 1)
|
|
->order('weigh desc, id asc')
|
|
->select();
|
|
|
|
$randomCount = min(4, count($randomPool));
|
|
$seed = crc32($userId . '_' . $today);
|
|
$selectedRandom = $this->_selectRandom($randomPool, $randomCount, $seed);
|
|
|
|
$allTasks = array_merge($fixedTasks, $selectedRandom);
|
|
usort($allTasks, function($a, $b) {
|
|
return $b['weigh'] - $a['weigh'];
|
|
});
|
|
|
|
$userTasks = Db::name('user_task')
|
|
->where('user_id', $userId)
|
|
->where('date', $today)
|
|
->column('task_id,progress,completed,claimed', 'task_id');
|
|
|
|
$result = [];
|
|
$completedCount = 0;
|
|
$claimedCount = 0;
|
|
$totalTasks = count($allTasks);
|
|
|
|
foreach ($allTasks as $task) {
|
|
$ut = isset($userTasks[$task['id']]) ? $userTasks[$task['id']] : null;
|
|
$progress = $ut ? intval($ut['progress']) : 0;
|
|
$completed = $ut ? intval($ut['completed']) : 0;
|
|
$claimed = $ut ? intval($ut['claimed']) : 0;
|
|
|
|
if ($completed) $completedCount++;
|
|
if ($claimed) $claimedCount++;
|
|
|
|
$result[] = [
|
|
'id' => $task['id'],
|
|
'name' => $task['name'],
|
|
'icon' => $task['icon'],
|
|
'type' => $task['type'],
|
|
'target' => intval($task['target']),
|
|
'action' => $task['action'],
|
|
'custom_url' => $task['custom_url'],
|
|
'custom_page' => $task['custom_page'],
|
|
'exp_reward' => intval($task['exp_reward']),
|
|
'score_reward' => intval($task['score_reward']),
|
|
'is_random' => intval($task['is_random']),
|
|
'progress' => $progress,
|
|
'completed' => $completed,
|
|
'claimed' => $claimed,
|
|
'percent' => $task['target'] > 0 ? min(100, intval($progress / $task['target'] * 100)) : 0,
|
|
];
|
|
}
|
|
|
|
$allCompleted = ($totalTasks > 0 && $completedCount === $totalTasks);
|
|
$perfectClaimed = false;
|
|
if ($allCompleted) {
|
|
$perfectLog = Db::name('user_exp_log')
|
|
->where('user_id', $userId)
|
|
->where('action', 'perfect_day')
|
|
->where('remark', 'like', '%' . $today . '%')
|
|
->find();
|
|
$perfectClaimed = $perfectLog ? true : false;
|
|
}
|
|
|
|
$this->success('', [
|
|
'tasks' => $result,
|
|
'total' => $totalTasks,
|
|
'completed' => $completedCount,
|
|
'claimed' => $claimedCount,
|
|
'is_perfect_day' => $allCompleted,
|
|
'perfect_claimed' => $perfectClaimed,
|
|
'date' => $today,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @name 上报任务进度
|
|
* @desc 上报某个任务的进度增量
|
|
* @param int task_id 任务ID
|
|
* @param int increment 进度增量(默认1)
|
|
*/
|
|
public function reportProgress()
|
|
{
|
|
$userId = $this->auth->id;
|
|
if (!$userId) $this->error('请先登录');
|
|
|
|
$taskId = intval(input('post.task_id', 0));
|
|
$increment = intval(input('post.increment', 1));
|
|
if (!$taskId) $this->error('参数错误');
|
|
if ($increment <= 0) $increment = 1;
|
|
|
|
$task = Db::name('daily_task')->where('id', $taskId)->where('status', 'normal')->find();
|
|
if (!$task) $this->error('任务不存在');
|
|
|
|
$today = date('Y-m-d');
|
|
$todayTasks = $this->_getTodayTaskIds($userId, $today);
|
|
if (!in_array($taskId, $todayTasks)) {
|
|
$this->error('今日无此任务');
|
|
}
|
|
|
|
$userTask = Db::name('user_task')
|
|
->where('user_id', $userId)
|
|
->where('task_id', $taskId)
|
|
->where('date', $today)
|
|
->find();
|
|
|
|
if ($userTask && $userTask['completed']) {
|
|
$this->success('任务已完成', [
|
|
'progress' => intval($userTask['progress']),
|
|
'completed' => 1,
|
|
'percent' => 100,
|
|
]);
|
|
}
|
|
|
|
if ($userTask) {
|
|
$newProgress = min(intval($userTask['progress']) + $increment, intval($task['target']));
|
|
$isCompleted = ($newProgress >= intval($task['target'])) ? 1 : 0;
|
|
Db::name('user_task')->where('id', $userTask['id'])->update([
|
|
'progress' => $newProgress,
|
|
'completed' => $isCompleted,
|
|
'updatetime' => time(),
|
|
]);
|
|
} else {
|
|
$newProgress = min($increment, intval($task['target']));
|
|
$isCompleted = ($newProgress >= intval($task['target'])) ? 1 : 0;
|
|
Db::name('user_task')->insert([
|
|
'user_id' => $userId,
|
|
'task_id' => $taskId,
|
|
'date' => $today,
|
|
'progress' => $newProgress,
|
|
'completed' => $isCompleted,
|
|
'claimed' => 0,
|
|
'createtime' => time(),
|
|
'updatetime' => time(),
|
|
]);
|
|
}
|
|
|
|
$this->success('进度已更新', [
|
|
'progress' => $newProgress,
|
|
'completed' => $isCompleted,
|
|
'percent' => intval($task['target']) > 0 ? min(100, intval($newProgress / intval($task['target']) * 100)) : 0,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @name 领取任务奖励
|
|
* @desc 领取已完成任务的EXP和积分奖励
|
|
* @param int task_id 任务ID
|
|
*/
|
|
public function claim()
|
|
{
|
|
$userId = $this->auth->id;
|
|
if (!$userId) $this->error('请先登录');
|
|
|
|
$taskId = intval(input('post.task_id', 0));
|
|
if (!$taskId) $this->error('参数错误');
|
|
|
|
$task = Db::name('daily_task')->where('id', $taskId)->find();
|
|
if (!$task) $this->error('任务不存在');
|
|
|
|
$today = date('Y-m-d');
|
|
$userTask = Db::name('user_task')
|
|
->where('user_id', $userId)
|
|
->where('task_id', $taskId)
|
|
->where('date', $today)
|
|
->find();
|
|
|
|
if (!$userTask) $this->error('今日无此任务进度');
|
|
if (!$userTask['completed']) $this->error('任务尚未完成');
|
|
if ($userTask['claimed']) $this->error('奖励已领取');
|
|
|
|
Db::name('user_task')->where('id', $userTask['id'])->update([
|
|
'claimed' => 1,
|
|
'updatetime' => time(),
|
|
]);
|
|
|
|
$expReward = intval($task['exp_reward']);
|
|
$scoreReward = intval($task['score_reward']);
|
|
|
|
if ($expReward > 0) {
|
|
\app\common\model\User::exp($expReward, $userId, 'task', '完成每日任务: ' . $task['name']);
|
|
}
|
|
if ($scoreReward > 0) {
|
|
\app\common\model\User::score($scoreReward, $userId, '完成每日任务: ' . $task['name']);
|
|
}
|
|
|
|
\app\api\controller\Achievement::checkBadges($userId);
|
|
|
|
$this->success('领取成功', [
|
|
'exp_reward' => $expReward,
|
|
'score_reward' => $scoreReward,
|
|
'task_name' => $task['name'],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @name 领取完美日奖励
|
|
* @desc 当天所有任务全部完成且领取后,可领取完美日额外奖励(20EXP+10积分)
|
|
*/
|
|
public function claimPerfect()
|
|
{
|
|
$userId = $this->auth->id;
|
|
if (!$userId) $this->error('请先登录');
|
|
|
|
$today = date('Y-m-d');
|
|
|
|
$perfectLog = Db::name('user_exp_log')
|
|
->where('user_id', $userId)
|
|
->where('action', 'perfect_day')
|
|
->where('remark', 'like', '%' . $today . '%')
|
|
->find();
|
|
if ($perfectLog) $this->error('今日完美日奖励已领取');
|
|
|
|
$todayTaskIds = $this->_getTodayTaskIds($userId, $today);
|
|
if (empty($todayTaskIds)) $this->error('今日无任务');
|
|
|
|
$claimedCount = Db::name('user_task')
|
|
->where('user_id', $userId)
|
|
->where('date', $today)
|
|
->where('task_id', 'in', $todayTaskIds)
|
|
->where('claimed', 1)
|
|
->count();
|
|
|
|
if ($claimedCount < count($todayTaskIds)) {
|
|
$this->error('还有任务未完成或未领取奖励');
|
|
}
|
|
|
|
\app\common\model\User::exp(20, $userId, 'perfect_day', '完美日奖励 ' . $today);
|
|
\app\common\model\User::score(10, $userId, '完美日奖励 ' . $today);
|
|
|
|
\app\api\controller\Achievement::checkBadges($userId);
|
|
|
|
$this->success('完美日奖励领取成功!', [
|
|
'exp_reward' => 20,
|
|
'score_reward' => 10,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @name 注册自定义任务
|
|
* @desc 客户端注册自定义任务(浏览URL/使用功能)
|
|
* @param string name 任务名称
|
|
* @param string icon 图标(emoji)
|
|
* @param string custom_url 自定义URL
|
|
* @param string custom_page 自定义页面标识
|
|
* @param int target 目标次数(默认1)
|
|
* @param int exp_reward EXP奖励(默认5)
|
|
* @param int score_reward 积分奖励(默认2)
|
|
*/
|
|
public function registerCustom()
|
|
{
|
|
$userId = $this->auth->id;
|
|
if (!$userId) $this->error('请先登录');
|
|
|
|
$name = input('post.name', '', 'trim');
|
|
$icon = input('post.icon', '🎯', 'trim');
|
|
$customUrl = input('post.custom_url', '', 'trim');
|
|
$customPage = input('post.custom_page', '', 'trim');
|
|
$target = intval(input('post.target', 1));
|
|
$expReward = intval(input('post.exp_reward', 5));
|
|
$scoreReward = intval(input('post.score_reward', 2));
|
|
|
|
if (!$name) $this->error('任务名称不能为空');
|
|
if (!$customUrl && !$customPage) $this->error('custom_url和custom_page至少填一个');
|
|
|
|
$existing = Db::name('daily_task')
|
|
->where('type', 'custom')
|
|
->where(function($query) use ($customUrl, $customPage) {
|
|
if ($customUrl) $query->whereOr('custom_url', $customUrl);
|
|
if ($customPage) $query->whereOr('custom_page', $customPage);
|
|
})
|
|
->find();
|
|
|
|
if ($existing) {
|
|
$this->success('任务已存在', [
|
|
'task_id' => $existing['id'],
|
|
'name' => $existing['name'],
|
|
]);
|
|
}
|
|
|
|
$id = Db::name('daily_task')->insertGetId([
|
|
'name' => $name,
|
|
'icon' => $icon,
|
|
'type' => 'custom',
|
|
'target' => max(1, $target),
|
|
'action' => 'custom',
|
|
'custom_url' => $customUrl,
|
|
'custom_page' => $customPage,
|
|
'exp_reward' => min(20, max(1, $expReward)),
|
|
'score_reward' => min(10, max(1, $scoreReward)),
|
|
'is_random' => 1,
|
|
'status' => 'normal',
|
|
'weigh' => 0,
|
|
'createtime' => time(),
|
|
'updatetime' => time(),
|
|
]);
|
|
|
|
$this->success('注册成功', ['task_id' => $id]);
|
|
}
|
|
|
|
/**
|
|
* @name 从随机池中选取N个任务(确定性种子)
|
|
*/
|
|
private function _selectRandom($pool, $count, $seed)
|
|
{
|
|
if (empty($pool) || $count <= 0) return [];
|
|
mt_srand($seed);
|
|
$shuffled = $pool;
|
|
shuffle($shuffled);
|
|
return array_slice($shuffled, 0, $count);
|
|
}
|
|
|
|
/**
|
|
* @name 获取今日分配给用户的任务ID列表
|
|
*/
|
|
private function _getTodayTaskIds($userId, $today)
|
|
{
|
|
$fixedTasks = Db::name('daily_task')
|
|
->where('status', 'normal')
|
|
->where('is_random', 0)
|
|
->column('id');
|
|
|
|
$randomPool = Db::name('daily_task')
|
|
->where('status', 'normal')
|
|
->where('is_random', 1)
|
|
->column('id');
|
|
|
|
$randomCount = min(4, count($randomPool));
|
|
$seed = crc32($userId . '_' . $today);
|
|
mt_srand($seed);
|
|
shuffle($randomPool);
|
|
$selectedRandom = array_slice($randomPool, 0, $randomCount);
|
|
|
|
return array_merge($fixedTasks, $selectedRandom);
|
|
}
|
|
}
|