Files
ulthon_admin/extend/base/admin/controller/NoticeBase.php

270 lines
9.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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) 强转只会取到前导年份(详情页显示 1970),必须取原始数据
'create_time' => (int) $row->getData('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('订阅设置已保存');
}
}