Files
xianyan/docs/toolsapi/application/api/controller/Task.php
Developer fc6fd7be0e feat: 完成v10.1.0版本大更新,新增密保系统、勋章、任务、排行榜等功能
### 变更详情
1. 新增密保问题系统,支持8种预置验证问题,多场景支持多验证方式
2. 新增勋章管理模块,包含勋章配置、用户勋章关联管理
3. 新增每日任务系统,支持任务配置和用户进度追踪
4. 新增赛季排行榜功能,支持周/月赛季排行与奖励结算
5. 新增信息流推荐权重配置管理
6. 重构服务路径分层,按设备/网络/数据分类管理服务
7. 优化Feed请求参数截断逻辑,避免URL过长
8. 新增等级工具类,统一处理等级颜色与称号展示
9. 新增屏幕共享共享信令Provider,复用传输服务实例
10. 新增Android/iOS分享适配与桌面小组件支持
11. 清理旧版测试脚本,新增部署维护脚本
12. 完善用户注销关联数据清理逻辑
2026-05-15 07:02:56 +08:00

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);
}
}