mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-11-08 03:50:50 +08:00
fix(notice): 发送事务包裹与软删过滤加固
This commit is contained in:
@@ -61,12 +61,15 @@ class NoticeBase extends AdminController
|
|||||||
->withJoin('notice', 'LEFT')
|
->withJoin('notice', 'LEFT')
|
||||||
->where('system_notice_target.admin_id', $adminId)
|
->where('system_notice_target.admin_id', $adminId)
|
||||||
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
||||||
|
// 软删通知不进个人域(管理页删除后此处消失)
|
||||||
|
->where('system_notice.delete_time', 0)
|
||||||
->where($where)
|
->where($where)
|
||||||
->count();
|
->count();
|
||||||
$list = $this->model
|
$list = $this->model
|
||||||
->withJoin('notice', 'LEFT')
|
->withJoin('notice', 'LEFT')
|
||||||
->where('system_notice_target.admin_id', $adminId)
|
->where('system_notice_target.admin_id', $adminId)
|
||||||
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
||||||
|
->where('system_notice.delete_time', 0)
|
||||||
->where($where)
|
->where($where)
|
||||||
->page($page, $limit)
|
->page($page, $limit)
|
||||||
->order($this->sort)
|
->order($this->sort)
|
||||||
@@ -95,6 +98,8 @@ class NoticeBase extends AdminController
|
|||||||
->withJoin('notice', 'LEFT')
|
->withJoin('notice', 'LEFT')
|
||||||
->where('system_notice_target.id', (int) $id)
|
->where('system_notice_target.id', (int) $id)
|
||||||
->where('system_notice_target.admin_id', $adminId)
|
->where('system_notice_target.admin_id', $adminId)
|
||||||
|
// 软删通知不进个人域(管理页删除后详情不可打开)
|
||||||
|
->where('system_notice.delete_time', 0)
|
||||||
->find();
|
->find();
|
||||||
if (empty($row)) {
|
if (empty($row)) {
|
||||||
$this->error('消息不存在或无权查看');
|
$this->error('消息不存在或无权查看');
|
||||||
|
|||||||
@@ -109,33 +109,39 @@ class NoticeServiceBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($adminIds === 'all') {
|
if ($adminIds === 'all') {
|
||||||
$adminIds = Db::name('system_admin')->where('status', 1)->column('id');
|
// delete_time=0 过滤软删管理员,与 sceneOverview 的 SystemAdmin 模型口径对齐
|
||||||
|
$adminIds = Db::name('system_admin')->where('status', 1)->where('delete_time', 0)->column('id');
|
||||||
} else {
|
} else {
|
||||||
$adminIds = array_values(array_unique(array_map('intval', (array) $adminIds)));
|
$adminIds = array_values(array_unique(array_map('intval', (array) $adminIds)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
$notice = SystemNotice::create([
|
// 主体创建与投递展开同事务:insertAll 中途异常时回滚,不残留零投递主体
|
||||||
'scene' => $scene,
|
[$notice, $targets] = Db::transaction(function () use ($adminIds, $channels, $content, $now, $options, $registered, $scene, $title) {
|
||||||
'title' => $title,
|
$notice = SystemNotice::create([
|
||||||
'content' => $content,
|
'scene' => $scene,
|
||||||
'biz_id' => (string) ($options['biz_id'] ?? ''),
|
'title' => $title,
|
||||||
'send_admin_id' => (int) ($options['send_admin_id'] ?? 0),
|
'content' => $content,
|
||||||
]);
|
'biz_id' => (string) ($options['biz_id'] ?? ''),
|
||||||
$noticeId = (int) $notice->id;
|
'send_admin_id' => (int) ($options['send_admin_id'] ?? 0),
|
||||||
|
]);
|
||||||
|
$noticeId = (int) $notice->id;
|
||||||
|
|
||||||
$targets = [];
|
$targets = [];
|
||||||
if ($adminIds !== []) {
|
if ($adminIds !== []) {
|
||||||
$targets = static::buildTargets($noticeId, $adminIds, $scene, $channels, $registered, $now);
|
$targets = static::buildTargets($noticeId, $adminIds, $scene, $channels, $registered, $now);
|
||||||
if ($targets !== []) {
|
if ($targets !== []) {
|
||||||
Db::name('system_notice_target')->insertAll($targets);
|
Db::name('system_notice_target')->insertAll($targets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return [$notice, $targets];
|
||||||
|
});
|
||||||
|
|
||||||
Event::trigger('NoticeSended', [$notice, $targets]);
|
Event::trigger('NoticeSended', [$notice, $targets]);
|
||||||
|
|
||||||
return $noticeId;
|
return (int) $notice->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,14 +246,18 @@ class NoticeServiceBase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 未读站内信数(channel=inmail AND read_time=0 AND status=2,走复合索引).
|
* 未读站内信数(channel=inmail AND read_time=0 AND status=2,走复合索引).
|
||||||
|
* JOIN 过滤软删通知:与个人域 mine/read 口径一致,管理页删除后未读数同步下降.
|
||||||
*/
|
*/
|
||||||
public static function unreadCount(int $adminId): int
|
public static function unreadCount(int $adminId): int
|
||||||
{
|
{
|
||||||
return Db::name('system_notice_target')
|
return Db::name('system_notice_target')
|
||||||
->where('admin_id', $adminId)
|
->alias('target')
|
||||||
->where('channel', self::CHANNEL_INMAIL)
|
->join('system_notice notice', 'notice.id = target.notice_id')
|
||||||
->where('read_time', 0)
|
->where('target.admin_id', $adminId)
|
||||||
->where('status', SystemNoticeTargetBase::STATUS_SUCCESS)
|
->where('target.channel', self::CHANNEL_INMAIL)
|
||||||
|
->where('target.read_time', 0)
|
||||||
|
->where('target.status', SystemNoticeTargetBase::STATUS_SUCCESS)
|
||||||
|
->where('notice.delete_time', 0)
|
||||||
->count();
|
->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use think\facade\Db;
|
|||||||
/**
|
/**
|
||||||
* NoticeService 核心行为集成测试.
|
* NoticeService 核心行为集成测试.
|
||||||
*
|
*
|
||||||
* 覆盖 11 个契约点(与计划任务 3 逐项对齐,每项一个 test 方法):
|
* 覆盖 13 个契约点(与计划任务 3 逐项对齐,每项一个 test 方法):
|
||||||
* 1. send 主体 + target 展开
|
* 1. send 主体 + target 展开
|
||||||
* 2. 'all' 广播(仅 status=1,含无 email 管理员)
|
* 2. 'all' 广播(仅 status=1,含无 email 管理员)
|
||||||
* 3. channels 缺省按 scene.channels 展开
|
* 3. channels 缺省按 scene.channels 展开
|
||||||
@@ -24,6 +24,8 @@ use think\facade\Db;
|
|||||||
* 9. inmail 全局总闸关闭跳过(键不存在视为开)
|
* 9. inmail 全局总闸关闭跳过(键不存在视为开)
|
||||||
* 10. unreadCount/markRead/markAllRead 数字正确
|
* 10. unreadCount/markRead/markAllRead 数字正确
|
||||||
* 11. markRead 越权防护
|
* 11. markRead 越权防护
|
||||||
|
* 12. 'all' 广播过滤软删管理员(F2-C10)
|
||||||
|
* 13. unreadCount 不受软删通知影响(F2-C6 个人域口径)
|
||||||
*
|
*
|
||||||
* fixture 均为事务内自造数据,tearDown 自动回滚;缓存清理显式执行
|
* fixture 均为事务内自造数据,tearDown 自动回滚;缓存清理显式执行
|
||||||
* (Cache 不受事务回滚影响,见 setInmailSwitch/clearInmailSwitch)。
|
* (Cache 不受事务回滚影响,见 setInmailSwitch/clearInmailSwitch)。
|
||||||
@@ -335,6 +337,44 @@ class NoticeServiceTest extends TestCase
|
|||||||
$this->assertSame(1, NoticeService::unreadCount($adminB), 'markAllRead 不得触碰他人行');
|
$this->assertSame(1, NoticeService::unreadCount($adminB), 'markAllRead 不得触碰他人行');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 12. 'all' 广播过滤软删管理员(F2-C10)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public function test_send_all_excludes_soft_deleted_admins(): void
|
||||||
|
{
|
||||||
|
$aliveAdmin = $this->createAdmin();
|
||||||
|
$softDeletedAdmin = $this->createAdmin(['delete_time' => time()]);
|
||||||
|
|
||||||
|
$noticeId = NoticeService::send('all', '软删过滤广播', '内容');
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, Db::name('system_notice_target')
|
||||||
|
->where('notice_id', $noticeId)->where('admin_id', $aliveAdmin)->count(), '在册管理员应收到广播');
|
||||||
|
$this->assertSame(
|
||||||
|
0,
|
||||||
|
Db::name('system_notice_target')->where('notice_id', $noticeId)->where('admin_id', $softDeletedAdmin)->count(),
|
||||||
|
'软删管理员不应收到广播'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 13. unreadCount 不受软删通知影响(F2-C6 个人域口径)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public function test_unread_count_ignores_soft_deleted_notices(): void
|
||||||
|
{
|
||||||
|
$adminId = $this->createAdmin();
|
||||||
|
|
||||||
|
NoticeService::send([$adminId], '正常通知', '内容');
|
||||||
|
$softDeletedNoticeId = NoticeService::send([$adminId], '将被删除的通知', '内容');
|
||||||
|
$this->assertSame(2, NoticeService::unreadCount($adminId));
|
||||||
|
|
||||||
|
// 管理页删除 = 软删 notice 主体(delete_time 置时间戳)
|
||||||
|
Db::name('system_notice')->where('id', $softDeletedNoticeId)->update(['delete_time' => time()]);
|
||||||
|
|
||||||
|
$this->assertSame(1, NoticeService::unreadCount($adminId), '软删通知不应计入未读数');
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// fixture 工厂(事务内运行,tearDown 自动回滚)
|
// fixture 工厂(事务内运行,tearDown 自动回滚)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user