feat: 优化消息互动中心与首页Banner呈现,同步全量业务数据与高清图标
这个提交包含在:
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户