mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 02:12:53 +00:00
feat: 添加任务浏览历史功能
- 在 UsersController 中新增获取、记录和清理任务浏览历史的 API 接口 - 创建 UserTaskBrowse 模型以管理用户的任务浏览记录 - 更新前端 Vue 组件以支持任务浏览历史的加载和显示 - 移除不再使用的本地缓存逻辑,直接通过 API 进行数据交互
This commit is contained in:
parent
6148b996d8
commit
0401b8a6e6
@ -15,6 +15,7 @@ use App\Module\Timer;
|
||||
use App\Ldap\LdapUser;
|
||||
use App\Models\Meeting;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\UserBot;
|
||||
use App\Models\WebSocket;
|
||||
use App\Models\UmengAlias;
|
||||
@ -28,6 +29,7 @@ use App\Models\UserDepartment;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\UserCheckinRecord;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Models\UserTaskBrowse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\UserEmailVerification;
|
||||
use App\Module\AgoraIO\AgoraTokenGenerator;
|
||||
@ -2718,4 +2720,109 @@ class UsersController extends AbstractController
|
||||
//
|
||||
return Base::retSuccess('保存成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/task/browse 43. 获取任务浏览历史
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName task__browse
|
||||
*
|
||||
* @apiParam {Number} [limit=20] 获取数量限制,最大50
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function task__browse()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$limit = min(intval(Request::input('limit', 20)), 50);
|
||||
//
|
||||
$browseHistory = UserTaskBrowse::getUserBrowseHistory($user->userid, $limit);
|
||||
|
||||
$data = [];
|
||||
foreach ($browseHistory as $browse) {
|
||||
if ($browse->task) {
|
||||
// 解析 flow_item_name 字段(格式:status|name|color)
|
||||
$flowItemParts = explode('|', $browse->task->flow_item_name ?: '');
|
||||
$flowItemStatus = $flowItemParts[0] ?? '';
|
||||
$flowItemName = $flowItemParts[1] ?? $browse->task->flow_item_name;
|
||||
$flowItemColor = $flowItemParts[2] ?? '';
|
||||
|
||||
$data[] = [
|
||||
'id' => $browse->task->id,
|
||||
'name' => $browse->task->name,
|
||||
'project_id' => $browse->task->project_id,
|
||||
'column_id' => $browse->task->column_id,
|
||||
'parent_id' => $browse->task->parent_id,
|
||||
'flow_item_id' => $browse->task->flow_item_id,
|
||||
'flow_item_name' => $flowItemName,
|
||||
'flow_item_status' => $flowItemStatus,
|
||||
'flow_item_color' => $flowItemColor,
|
||||
'complete_at' => $browse->task->complete_at,
|
||||
'browsed_at' => $browse->browsed_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/users/task/browse_save 44. 记录任务浏览历史
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName task__browse_save
|
||||
*
|
||||
* @apiParam {Number} task_id 任务ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function task__browse_save()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$task_id = intval(Request::input('task_id'));
|
||||
if ($task_id <= 0) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
//
|
||||
ProjectTask::userTask($task_id, null, null);
|
||||
//
|
||||
UserTaskBrowse::recordBrowse($user->userid, $task_id);
|
||||
//
|
||||
return Base::retSuccess('记录成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/users/task/browse_clean 45. 清理任务浏览历史
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName task__browse_clean
|
||||
*
|
||||
* @apiParam {Number} [keep_count=100] 保留记录数量,0表示全部清理
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function task__browse_clean()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$keepCount = intval(Request::input('keep_count', 100));
|
||||
//
|
||||
$deletedCount = UserTaskBrowse::cleanUserBrowseHistory($user->userid, $keepCount);
|
||||
//
|
||||
return Base::retSuccess('清理完成', ['deleted_count' => $deletedCount]);
|
||||
}
|
||||
}
|
||||
|
||||
128
app/Models/UserTaskBrowse.php
Normal file
128
app/Models/UserTaskBrowse.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* App\Models\UserTaskBrowse
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $userid 用户ID
|
||||
* @property int $task_id 任务ID
|
||||
* @property \Illuminate\Support\Carbon|null $browsed_at 浏览时间
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\ProjectTask|null $task
|
||||
* @property-read \App\Models\User|null $user
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereBrowsedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereTaskId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserTaskBrowse whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class UserTaskBrowse extends AbstractModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'userid',
|
||||
'task_id',
|
||||
'browsed_at',
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
'browsed_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联用户
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'userid', 'userid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联任务
|
||||
*/
|
||||
public function task()
|
||||
{
|
||||
return $this->belongsTo(ProjectTask::class, 'task_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录用户浏览任务
|
||||
* @param int $userid 用户ID
|
||||
* @param int $task_id 任务ID
|
||||
* @return UserTaskBrowse
|
||||
*/
|
||||
public static function recordBrowse($userid, $task_id)
|
||||
{
|
||||
return self::updateOrCreate(
|
||||
[
|
||||
'userid' => $userid,
|
||||
'task_id' => $task_id,
|
||||
],
|
||||
[
|
||||
'browsed_at' => Carbon::now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户浏览历史
|
||||
* @param int $userid 用户ID
|
||||
* @param int $limit 获取数量
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getUserBrowseHistory($userid, $limit = 20)
|
||||
{
|
||||
return self::with(['task' => function ($query) {
|
||||
$query->select([
|
||||
'id', 'name', 'project_id', 'column_id', 'parent_id',
|
||||
'flow_item_id', 'flow_item_name',
|
||||
'complete_at', 'archived_at'
|
||||
]);
|
||||
}])
|
||||
->whereUserid($userid)
|
||||
->whereHas('task', function ($query) {
|
||||
// 只获取存在且未被删除的任务
|
||||
$query->whereNull('archived_at');
|
||||
})
|
||||
->orderByDesc('browsed_at')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户浏览历史
|
||||
* @param int $userid 用户ID
|
||||
* @param int $keepCount 保留数量,0表示全部删除
|
||||
* @return int 删除的记录数
|
||||
*/
|
||||
public static function cleanUserBrowseHistory($userid, $keepCount = 100)
|
||||
{
|
||||
if ($keepCount === 0) {
|
||||
return self::whereUserid($userid)->delete();
|
||||
}
|
||||
|
||||
$keepIds = self::whereUserid($userid)
|
||||
->orderByDesc('browsed_at')
|
||||
->limit($keepCount)
|
||||
->pluck('id');
|
||||
|
||||
return self::whereUserid($userid)
|
||||
->whereNotIn('id', $keepIds)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserTaskBrowsesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('user_task_browses'))
|
||||
return;
|
||||
|
||||
Schema::create('user_task_browses', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('userid')->index()->nullable()->default(0)->comment('用户ID');
|
||||
$table->bigInteger('task_id')->index()->nullable()->default(0)->comment('任务ID');
|
||||
$table->timestamp('browsed_at')->index()->nullable()->comment('浏览时间');
|
||||
$table->timestamps();
|
||||
|
||||
// 复合索引:用户ID + 浏览时间(用于按时间排序获取用户浏览历史)
|
||||
$table->index(['userid', 'browsed_at']);
|
||||
// 唯一索引:用户ID + 任务ID(防止重复记录,相同任务会更新浏览时间)
|
||||
$table->unique(['userid', 'task_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_task_browses');
|
||||
}
|
||||
}
|
||||
@ -487,6 +487,9 @@ export default {
|
||||
approveShow: false,
|
||||
approveDetails: {id: 0},
|
||||
approveDetailsShow: false,
|
||||
|
||||
taskBrowseLoading: false,
|
||||
taskBrowseHistory: [], // 存储任务浏览历史
|
||||
}
|
||||
},
|
||||
|
||||
@ -537,7 +540,6 @@ export default {
|
||||
'columnTemplate',
|
||||
|
||||
'clientNewVersion',
|
||||
'cacheTaskBrowse',
|
||||
|
||||
'reportUnreadNumber',
|
||||
'approveUnreadNumber',
|
||||
@ -708,10 +710,8 @@ export default {
|
||||
},
|
||||
|
||||
taskBrowseLists() {
|
||||
const {cacheTasks, cacheTaskBrowse, userId} = this;
|
||||
return cacheTaskBrowse.filter(({userid}) => userid === userId).map(({id}) => {
|
||||
return cacheTasks.find(task => task.id === id) || {}
|
||||
});
|
||||
// 直接使用组件内的响应式数据
|
||||
return this.taskBrowseHistory.slice(0, 10); // 只显示前10个
|
||||
},
|
||||
},
|
||||
|
||||
@ -912,6 +912,10 @@ export default {
|
||||
|
||||
menuVisibleChange(visible) {
|
||||
this.visibleMenu = visible
|
||||
// 当菜单展开时,获取最新的浏览历史
|
||||
if (visible && !this.taskBrowseLoading) {
|
||||
this.loadTaskBrowseHistory()
|
||||
}
|
||||
},
|
||||
|
||||
classNameRoute(path) {
|
||||
@ -1387,6 +1391,24 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载任务浏览历史
|
||||
*/
|
||||
loadTaskBrowseHistory() {
|
||||
if (this.taskBrowseLoading) return
|
||||
|
||||
this.taskBrowseLoading = true
|
||||
this.$store.dispatch("getTaskBrowseHistory", 20).then(({data}) => {
|
||||
// 更新组件内的浏览历史数据
|
||||
this.taskBrowseHistory = data || []
|
||||
}).catch(error => {
|
||||
console.warn('获取任务浏览历史失败:', error)
|
||||
// 失败时保持当前数据不变
|
||||
}).finally(() => {
|
||||
this.taskBrowseLoading = false
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
47
resources/assets/js/store/actions.js
vendored
47
resources/assets/js/store/actions.js
vendored
@ -1076,7 +1076,6 @@ export default {
|
||||
cacheProjectParameter: await $A.IDBArray("cacheProjectParameter"),
|
||||
cacheLoginEmail: await $A.IDBString("cacheLoginEmail"),
|
||||
cacheFileSort: await $A.IDBJson("cacheFileSort"),
|
||||
cacheTaskBrowse: await $A.IDBArray("cacheTaskBrowse"),
|
||||
cacheTranslationLanguage: await $A.IDBString("cacheTranslationLanguage"),
|
||||
cacheTranscriptionLanguage: await $A.IDBString("cacheTranscriptionLanguage"),
|
||||
cacheTranslations: await $A.IDBArray("cacheTranslations"),
|
||||
@ -1124,7 +1123,6 @@ export default {
|
||||
'cacheColumns',
|
||||
'cacheTasks',
|
||||
'cacheProjectParameter',
|
||||
'cacheTaskBrowse',
|
||||
'cacheTranslations',
|
||||
'dialogMsgs',
|
||||
'dialogDrafts',
|
||||
@ -2746,23 +2744,38 @@ export default {
|
||||
|
||||
/**
|
||||
* 保存任务浏览记录
|
||||
* @param state
|
||||
* @param dispatch
|
||||
* @param task_id
|
||||
*/
|
||||
saveTaskBrowse({state}, task_id) {
|
||||
const index = state.cacheTaskBrowse.findIndex(({id}) => id == task_id)
|
||||
if (index > -1) {
|
||||
state.cacheTaskBrowse.splice(index, 1)
|
||||
}
|
||||
state.cacheTaskBrowse.unshift({
|
||||
id: task_id,
|
||||
userid: state.userId
|
||||
})
|
||||
if (state.cacheTaskBrowse.length > 200) {
|
||||
state.cacheTaskBrowse.splice(200);
|
||||
}
|
||||
//
|
||||
$A.IDBSave("cacheTaskBrowse", state.cacheTaskBrowse);
|
||||
saveTaskBrowse({dispatch}, task_id) {
|
||||
// 直接调用API保存到远程,不维护本地缓存
|
||||
dispatch('call', {
|
||||
url: 'users/task/browse_save',
|
||||
data: {
|
||||
task_id: task_id
|
||||
},
|
||||
method: 'post',
|
||||
spinner: 0, // 静默调用,不显示loading
|
||||
}).catch(error => {
|
||||
console.warn('保存任务浏览历史失败:', error);
|
||||
// API失败时不影响用户体验,只记录错误
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取任务浏览历史
|
||||
* @param dispatch
|
||||
* @param limit
|
||||
*/
|
||||
getTaskBrowseHistory({dispatch}, limit = 20) {
|
||||
return dispatch('call', {
|
||||
url: 'users/task/browse',
|
||||
data: {
|
||||
limit: limit
|
||||
},
|
||||
method: 'get',
|
||||
spinner: 0, // 静默调用
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
1
resources/assets/js/store/state.js
vendored
1
resources/assets/js/store/state.js
vendored
@ -102,7 +102,6 @@ export default {
|
||||
cacheColumns: [],
|
||||
cacheTasks: [],
|
||||
cacheProjectParameter: [],
|
||||
cacheTaskBrowse: [],
|
||||
|
||||
// Emoji
|
||||
cacheEmojis: [],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user