mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-11-07 19:40:49 +08:00
268 lines
9.0 KiB
PHP
268 lines
9.0 KiB
PHP
<?php
|
||
|
||
namespace base\admin\controller;
|
||
|
||
use app\admin\model\SystemNoticeTarget;
|
||
use app\admin\service\annotation\ControllerAnnotation;
|
||
use app\admin\service\annotation\NodeAnotation;
|
||
use app\common\controller\AdminController;
|
||
use app\common\service\NoticeService;
|
||
use think\App;
|
||
use think\facade\Db;
|
||
|
||
/**
|
||
* 消息中心(个人域).
|
||
*
|
||
* 我的站内信、订阅设置等个人数据动作,登录即可访问(auth=false)。
|
||
* 安全铁律:一切查询与写入强制 admin_id=当前会话管理员,杜绝越权。
|
||
*
|
||
* @ControllerAnnotation(title="消息中心", module="通用")
|
||
*/
|
||
class NoticeBase extends AdminController
|
||
{
|
||
/**
|
||
* 订阅设置矩阵提供的渠道(列).
|
||
*/
|
||
protected const SUBSCRIBE_CHANNELS = [
|
||
NoticeService::CHANNEL_INMAIL => '站内信',
|
||
NoticeService::CHANNEL_EMAIL => '邮箱',
|
||
];
|
||
|
||
/**
|
||
* @param App $app
|
||
*/
|
||
public function __construct(App $app)
|
||
{
|
||
parent::__construct($app);
|
||
|
||
$this->model = new SystemNoticeTarget();
|
||
|
||
// 场景中文映射(mine 列表场景下拉/展示用)
|
||
$sceneOptions = [];
|
||
foreach (NoticeService::scenes() as $scene => $info) {
|
||
$sceneOptions[$scene] = $info['title'];
|
||
}
|
||
$this->assign('select_list_notice_scene', $sceneOptions, true);
|
||
}
|
||
|
||
/**
|
||
* 我的站内信列表.
|
||
* @NodeAnotation(title="我的消息", auth=false)
|
||
*/
|
||
public function mine()
|
||
{
|
||
if ($this->request->isAjax()) {
|
||
if (input('selectFields')) {
|
||
return $this->selectList();
|
||
}
|
||
list($page, $limit, $where) = $this->buildTableParames();
|
||
$adminId = (int) $this->getAdminId();
|
||
$count = $this->model
|
||
->withJoin('notice', 'LEFT')
|
||
->where('system_notice_target.admin_id', $adminId)
|
||
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
||
->where($where)
|
||
->count();
|
||
$list = $this->model
|
||
->withJoin('notice', 'LEFT')
|
||
->where('system_notice_target.admin_id', $adminId)
|
||
->where('system_notice_target.channel', NoticeService::CHANNEL_INMAIL)
|
||
->where($where)
|
||
->page($page, $limit)
|
||
->order($this->sort)
|
||
->select();
|
||
$data = [
|
||
'code' => 0,
|
||
'msg' => '',
|
||
'count' => $count,
|
||
'data' => $list,
|
||
];
|
||
|
||
return json($data);
|
||
}
|
||
|
||
return $this->fetch();
|
||
}
|
||
|
||
/**
|
||
* 消息详情(进入即自动标记已读;先校验归属再标记,防越权).
|
||
* @NodeAnotation(title="消息详情", auth=false)
|
||
*/
|
||
public function read($id)
|
||
{
|
||
$adminId = (int) $this->getAdminId();
|
||
$row = $this->model
|
||
->withJoin('notice', 'LEFT')
|
||
->where('system_notice_target.id', (int) $id)
|
||
->where('system_notice_target.admin_id', $adminId)
|
||
->find();
|
||
if (empty($row)) {
|
||
$this->error('消息不存在或无权查看');
|
||
}
|
||
|
||
if (empty($row->read_time)) {
|
||
NoticeService::markRead([(int) $id], $adminId);
|
||
$row->read_time = time();
|
||
}
|
||
|
||
// 提取平铺变量供模板渲染(notice 关联模型直接取值,规避模板链式访问歧义)
|
||
$notice = $row->notice;
|
||
$sceneTitleMap = [];
|
||
foreach (NoticeService::scenes() as $scene => $info) {
|
||
$sceneTitleMap[$scene] = $info['title'];
|
||
}
|
||
$msg = [
|
||
'id' => (int) $row->id,
|
||
'title' => (string) ($notice->title ?? '(通知已删除)'),
|
||
'scene_title' => $sceneTitleMap[(string) ($notice->scene ?? '')] ?? (string) ($notice->scene ?? '-'),
|
||
'content' => (string) ($notice->content ?? ''),
|
||
'read_time' => (int) $row->read_time,
|
||
'create_time' => (int) $row->create_time,
|
||
];
|
||
$this->assign('msg', $msg);
|
||
|
||
return $this->fetch();
|
||
}
|
||
|
||
/**
|
||
* 标记已读(批量).
|
||
* @NodeAnotation(title="标记已读", auth=false)
|
||
*/
|
||
public function markRead()
|
||
{
|
||
$this->checkPostRequest();
|
||
$targetIds = $this->request->post('target_ids');
|
||
if (empty($targetIds)) {
|
||
// 表格勾选批量提交(data-request+checkbox)的默认字段名是 id
|
||
$targetIds = $this->request->post('id');
|
||
}
|
||
$targetIds = is_array($targetIds) ? $targetIds : explode(',', (string) $targetIds);
|
||
$targetIds = array_values(array_filter(array_map('intval', $targetIds)));
|
||
|
||
$count = NoticeService::markRead($targetIds, (int) $this->getAdminId());
|
||
|
||
$this->success('已标记 ' . $count . ' 条消息为已读');
|
||
}
|
||
|
||
/**
|
||
* 全部站内信标记已读.
|
||
* @NodeAnotation(title="全部标记已读", auth=false)
|
||
*/
|
||
public function markAllRead()
|
||
{
|
||
$this->checkPostRequest();
|
||
NoticeService::markAllRead((int) $this->getAdminId());
|
||
|
||
$this->success('已全部标记为已读');
|
||
}
|
||
|
||
/**
|
||
* 未读站内信数(顶栏铃铛轮询接口).
|
||
* @NodeAnotation(title="未读消息数", auth=false)
|
||
*/
|
||
public function unreadCount()
|
||
{
|
||
$count = NoticeService::unreadCount((int) $this->getAdminId());
|
||
|
||
$this->success(null, ['count' => $count]);
|
||
}
|
||
|
||
/**
|
||
* 订阅设置页(场景×渠道矩阵,纯渲染不写库).
|
||
*
|
||
* 无记录=默认订阅;locked 场景恒选中且置灰(必选)。
|
||
* @NodeAnotation(title="订阅设置", auth=false)
|
||
*/
|
||
public function subscribe()
|
||
{
|
||
$adminId = (int) $this->getAdminId();
|
||
|
||
$rows = Db::name('system_notice_subscribe')
|
||
->where('admin_id', $adminId)
|
||
->select();
|
||
$subscribeMap = [];
|
||
foreach ($rows as $row) {
|
||
$subscribeMap[$row['scene'] . '|' . $row['channel']] = (int) $row['status'];
|
||
}
|
||
|
||
$matrix = [];
|
||
foreach (NoticeService::scenes() as $scene => $info) {
|
||
$locked = !empty($info['locked']);
|
||
$cells = [];
|
||
foreach (array_keys(self::SUBSCRIBE_CHANNELS) as $channel) {
|
||
$checked = $locked || ($subscribeMap[$scene . '|' . $channel] ?? 1) === 1;
|
||
$cells[] = [
|
||
'name' => 'matrix[' . $scene . '][' . $channel . ']',
|
||
'checked' => $checked ? 'checked' : '',
|
||
'disabled' => $locked ? 'disabled' : '',
|
||
];
|
||
}
|
||
$matrix[] = [
|
||
'scene' => $scene,
|
||
'title' => $info['title'],
|
||
'locked' => $locked,
|
||
'cells' => $cells,
|
||
];
|
||
}
|
||
|
||
$this->assign('matrix', $matrix);
|
||
$this->assign('channels', self::SUBSCRIBE_CHANNELS);
|
||
|
||
return $this->fetch();
|
||
}
|
||
|
||
/**
|
||
* 保存订阅设置(POST matrix:逐场景×渠道 upsert).
|
||
*
|
||
* locked 场景直接忽略不入库(不可关闭语义,不报错);
|
||
* 仅写当前会话管理员的订阅记录。
|
||
* @NodeAnotation(title="保存订阅设置", auth=false)
|
||
*/
|
||
public function saveSubscribe()
|
||
{
|
||
$this->checkPostRequest();
|
||
$adminId = (int) $this->getAdminId();
|
||
$matrix = (array) $this->request->post('matrix');
|
||
if ($matrix === []) {
|
||
$this->error('没有需要保存的订阅设置');
|
||
}
|
||
|
||
$scenes = NoticeService::scenes();
|
||
$now = time();
|
||
|
||
foreach ($matrix as $scene => $channels) {
|
||
// 仅注册场景可保存;locked 场景忽略(不写任何记录)
|
||
if (!isset($scenes[$scene]) || !empty($scenes[$scene]['locked'])) {
|
||
continue;
|
||
}
|
||
foreach ((array) $channels as $channel => $value) {
|
||
if (!isset(self::SUBSCRIBE_CHANNELS[$channel])) {
|
||
continue;
|
||
}
|
||
$status = ((int) $value === 1) ? 1 : 0;
|
||
$row = Db::name('system_notice_subscribe')
|
||
->where('admin_id', $adminId)
|
||
->where('scene', (string) $scene)
|
||
->where('channel', (string) $channel)
|
||
->find();
|
||
if ($row) {
|
||
Db::name('system_notice_subscribe')
|
||
->where('id', $row['id'])
|
||
->update(['status' => $status, 'update_time' => $now]);
|
||
} else {
|
||
Db::name('system_notice_subscribe')->insert([
|
||
'admin_id' => $adminId,
|
||
'scene' => (string) $scene,
|
||
'channel' => (string) $channel,
|
||
'status' => $status,
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
$this->success('订阅设置已保存');
|
||
}
|
||
}
|