feat: 优化消息互动中心与首页Banner呈现,同步全量业务数据与高清图标

这个提交包含在:
2026-10-24 11:39:44 +08:00
父节点 0f84426c33
当前提交 d18b597010
共有 109 个文件被更改,包括 3385 次插入 和 526 次删除

查看文件

@@ -7,11 +7,16 @@ use think\Db;
/**
* 用户端 · 消息中心(Module-MESSAGE)
* 路由:
* GET /api/v1/message/list M1 消息列表
* POST /api/v1/message/:id/read M2 标记已读
* GET /api/v1/message/unread-count M3 未读消息数(首页角标)
* GET /api/v1/message/list M1 消息列表(支持个人消息与系统广播)
* POST /api/v1/message/:id/read M2 标记单条已读
* GET /api/v1/message/unread-count M3 未读消息数(首页角标统计)
* POST /api/v1/message/read-all M4 一键已读(批量标记)
* POST /api/v1/message/clear M5 一键清理(批量软删除/屏蔽)
* POST /api/v1/message/:id/delete M6 删除单条消息(软删除/屏蔽)
*
* 说明:user_id=0 表示全体广播消息,用户可见。
* 说明:
* - user_id > 0 为用户专属通知,支持物理或软删除(deletetime)。
* - user_id = 0 为全站系统广播,利用 read_uids 和 deleted_uids 支持多用户独立已读与独立清理,互不影响。
*/
class Message extends V1
{
@@ -19,6 +24,8 @@ class Message extends V1
* M1 消息列表
* GET /api/v1/message/list 入参 {type?,page,size}
* → 分页 {list:[{id,type,title,content,read,createdAt}],page,size,total,hasMore}
*
* @return void
*/
public function list()
{
@@ -28,8 +35,17 @@ class Message extends V1
// 闭包重建查询:TP5.0 的 count() 会清空 Query::options,不能复用同一对象
list($rows, $total, $page, $size) = $this->pageQuery(function ($forCount) use ($uid, $type) {
$q = Db::name('fc_message')->where('user_id', 'in', [$uid, 0]);
if ($type !== '' && $type !== null) {
$q = Db::name('fc_message')
->whereNull('deletetime')
->where(function ($sub) use ($uid) {
$sub->where('user_id', $uid)
->whereOr(function ($sub2) use ($uid) {
$sub2->where('user_id', 0)
->whereRaw("NOT FIND_IN_SET('{$uid}', IFNULL(deleted_uids, ''))");
});
});
if ($type !== '' && $type !== null && $type !== 'all') {
$q->where('type', $type);
}
if ($forCount) {
@@ -40,12 +56,21 @@ class Message extends V1
$list = [];
foreach ($rows as $row) {
// 计算当前用户下的已读状态(广播消息匹配 read_uids 集合)
$isRead = false;
if ((int)$row['user_id'] === $uid) {
$isRead = (bool)(int)$row['isread'];
} else {
$readUids = array_filter(explode(',', (string)$row['read_uids']));
$isRead = (bool)(int)$row['isread'] || in_array((string)$uid, $readUids);
}
$list[] = [
'id' => (int)$row['id'],
'type' => (string)$row['type'],
'title' => (string)$row['title'],
'content' => (string)$row['content'],
'read' => (bool)(int)$row['isread'],
'read' => $isRead,
'createdAt' => $this->iso($row['createtime']),
];
}
@@ -54,8 +79,10 @@ class Message extends V1
}
/**
* M2 标记已读
* M2 标记单条已读
* POST /api/v1/message/:id/read → {read:true}
*
* @return void
*/
public function read()
{
@@ -63,9 +90,31 @@ class Message extends V1
$uid = (int)$user['id'];
$id = (int)$this->request->param('id', 0);
Db::name('fc_message')->where('id', $id)
->where('user_id', 'in', [$uid, 0])
->update(['isread' => 1, 'readtime' => time()]);
$msg = Db::name('fc_message')->where('id', $id)->whereNull('deletetime')->find();
if (!$msg) {
$this->fail(404, '消息不存在或已删除');
}
$now = time();
if ((int)$msg['user_id'] === $uid) {
Db::name('fc_message')->where('id', $id)->update([
'isread' => 1,
'readtime' => $now,
'updatetime' => $now,
]);
} elseif ((int)$msg['user_id'] === 0) {
// 系统广播:维护用户级已读集合 read_uids
$readUids = array_filter(explode(',', (string)$msg['read_uids']));
if (!in_array((string)$uid, $readUids)) {
$readUids[] = (string)$uid;
Db::name('fc_message')->where('id', $id)->update([
'read_uids' => implode(',', array_unique($readUids)),
'updatetime' => $now,
]);
}
} else {
$this->fail(403, '无权操作此消息');
}
$this->ok(['read' => true]);
}
@@ -73,19 +122,184 @@ class Message extends V1
/**
* M3 未读消息数(首页消息角标)
* GET /api/v1/message/unread-count → {count}
* 规则:统计当前用户可见(user_id = 本人 或 0 广播)且 isread=0 的消息条数,
* 替代前端原先写死的角标数字。
* 规则:统计个人专属未读数 + 全体广播中未被当前用户读取的有效条数
*
* @return void
*/
public function unreadCount()
{
$user = $this->auth();
$uid = (int)$user['id'];
$count = (int)Db::name('fc_message')
->where('user_id', 'in', [$uid, 0])
// 1. 专属个人消息未读数
$userCount = (int)Db::name('fc_message')
->where('user_id', $uid)
->where('isread', 0)
->whereNull('deletetime')
->count();
$this->ok(['count' => $count]);
// 2. 系统广播未读数
$broadcastMsgs = Db::name('fc_message')
->where('user_id', 0)
->whereNull('deletetime')
->select();
$broadcastCount = 0;
foreach ($broadcastMsgs as $b) {
$deletedUids = array_filter(explode(',', (string)$b['deleted_uids']));
if (in_array((string)$uid, $deletedUids)) {
continue; // 用户已删除该广播
}
if ((int)$b['isread'] === 1) {
continue; // 广播全局标记已读
}
$readUids = array_filter(explode(',', (string)$b['read_uids']));
if (!in_array((string)$uid, $readUids)) {
$broadcastCount++;
}
}
$this->ok(['count' => $userCount + $broadcastCount]);
}
/**
* M4 一键已读
* POST /api/v1/message/read-all 入参 {type?}
* 规则:将当前分类下的专属未读消息及有效广播全部标记为已读
*
* @return void
*/
public function readAll()
{
$user = $this->auth();
$uid = (int)$user['id'];
$type = $this->input('type', '');
$now = time();
// 1. 批量标记专属个人消息
$q = Db::name('fc_message')
->where('user_id', $uid)
->where('isread', 0)
->whereNull('deletetime');
if ($type !== '' && $type !== null && $type !== 'all') {
$q->where('type', $type);
}
$affected = $q->update([
'isread' => 1,
'readtime' => $now,
'updatetime' => $now,
]);
// 2. 批量将有效系统广播加入当前用户已读
$broadQ = Db::name('fc_message')->where('user_id', 0)->whereNull('deletetime');
if ($type !== '' && $type !== null && $type !== 'all') {
$broadQ->where('type', $type);
}
$broadcasts = $broadQ->select();
foreach ($broadcasts as $b) {
$delUids = array_filter(explode(',', (string)$b['deleted_uids']));
if (in_array((string)$uid, $delUids)) {
continue;
}
$readUids = array_filter(explode(',', (string)$b['read_uids']));
if (!in_array((string)$uid, $readUids)) {
$readUids[] = (string)$uid;
Db::name('fc_message')->where('id', $b['id'])->update([
'read_uids' => implode(',', array_unique($readUids)),
'updatetime' => $now,
]);
$affected++;
}
}
$this->ok(['success' => true, 'affected' => (int)$affected]);
}
/**
* M5 一键清理
* POST /api/v1/message/clear 入参 {type?}
* 规则:对专属通知执行软删除(deletetime);对广播消息记录当前用户已删除集合(deleted_uids)
*
* @return void
*/
public function clear()
{
$user = $this->auth();
$uid = (int)$user['id'];
$type = $this->input('type', '');
$now = time();
// 1. 专属消息软删除
$q = Db::name('fc_message')->where('user_id', $uid)->whereNull('deletetime');
if ($type !== '' && $type !== null && $type !== 'all') {
$q->where('type', $type);
}
$affected = $q->update([
'deletetime' => $now,
'updatetime' => $now,
]);
// 2. 广播消息:将 uid 追加到 deleted_uids
$broadQ = Db::name('fc_message')->where('user_id', 0)->whereNull('deletetime');
if ($type !== '' && $type !== null && $type !== 'all') {
$broadQ->where('type', $type);
}
$broadcasts = $broadQ->select();
foreach ($broadcasts as $b) {
$delUids = array_filter(explode(',', (string)$b['deleted_uids']));
if (!in_array((string)$uid, $delUids)) {
$delUids[] = (string)$uid;
Db::name('fc_message')->where('id', $b['id'])->update([
'deleted_uids' => implode(',', array_unique($delUids)),
'updatetime' => $now,
]);
$affected++;
}
}
$this->ok(['success' => true, 'affected' => (int)$affected]);
}
/**
* M6 删除单条消息
* POST /api/v1/message/:id/delete
* 规则:对专属消息软删除(deletetime);对广播消息加入 deleted_uids
*
* @return void
*/
public function delete()
{
$user = $this->auth();
$uid = (int)$user['id'];
$id = (int)$this->request->param('id', 0);
$now = time();
$msg = Db::name('fc_message')->where('id', $id)->whereNull('deletetime')->find();
if (!$msg) {
$this->fail(404, '消息不存在或已删除');
}
if ((int)$msg['user_id'] === $uid) {
// 专属个人消息执行软删除
Db::name('fc_message')->where('id', $id)->update([
'deletetime' => $now,
'updatetime' => $now,
]);
} elseif ((int)$msg['user_id'] === 0) {
// 广播消息:记录已删除该消息的用户 UID
$delUids = array_filter(explode(',', (string)$msg['deleted_uids']));
if (!in_array((string)$uid, $delUids)) {
$delUids[] = (string)$uid;
Db::name('fc_message')->where('id', $id)->update([
'deleted_uids' => implode(',', array_unique($delUids)),
'updatetime' => $now,
]);
}
} else {
$this->fail(403, '无权操作此消息');
}
$this->ok(['success' => true]);
}
}