model = new \app\admin\model\Purchasecontent; $this->ensurePurchaseContentTables(); } protected function ensurePurchaseContentTables(): void { try { Db::query('SELECT 1 FROM `purchase_content` LIMIT 1'); Db::query('SELECT 1 FROM `purchase_content_recipient` LIMIT 1'); } catch (\Throwable $e) { $sqlFile = APP_PATH . 'extra' . DS . 'purchase_content_install.sql'; if (is_file($sqlFile)) { $sql = file_get_contents($sqlFile); if (is_string($sql) && $sql !== '') { foreach (preg_split('/;\s*[\r\n]+/', $sql) as $stmt) { $stmt = trim($stmt); if ($stmt === '' || stripos($stmt, 'CREATE TABLE') === false) { continue; } try { Db::execute($stmt); } catch (\Throwable $ignore) { } } } } } } /** * 选择后台用户(发送人 / 接收人) */ public function adminselect() { $this->model = model('Admin'); $this->selectpageFields = 'id,username,nickname'; $this->searchFields = 'id,username,nickname'; $this->model->where('status', 'normal'); return $this->selectpage(); } public function index() { $this->relationSearch = false; $this->request->filter(['strip_tags', 'trim']); if (!$this->request->isAjax()) { return $this->view->fetch(); } $adminId = (int)$this->auth->id; $isSuper = $this->auth->isSuperAdmin(); list($where, $sort, $order, $offset, $limit) = $this->buildparams(); $query = $this->model->where($where); if (!$isSuper) { $recvIds = Db::table('purchase_content_recipient') ->where('admin_id', $adminId) ->column('content_id'); $recvIds = is_array($recvIds) ? array_values(array_unique(array_filter(array_map('intval', $recvIds)))) : []; $query->where(function ($q) use ($adminId, $recvIds) { $q->where('creator_id', $adminId); if ($recvIds !== []) { $q->whereOr('id', 'in', $recvIds); } }); } $list = $query->order($sort, $order)->paginate($limit); $rows = $list->items(); $this->enrichContentRows($rows, $adminId); return json(['total' => $list->total(), 'rows' => $rows]); } public function add() { if (!$this->request->isPost()) { $admin = $this->auth->getUserinfo(); $this->view->assign('admin', [ 'id' => (int)($admin['id'] ?? $this->auth->id), 'nickname' => (string)($admin['nickname'] ?? $admin['username'] ?? ''), ]); $this->view->assign('adminList', $this->loadNormalAdminList()); return $this->view->fetch(); } $params = $this->request->post('row/a'); if (empty($params)) { $this->error(__('Parameter %s can not be empty', '')); } $subject = trim((string)($params['subject'] ?? '')); $content = trim((string)($params['content'] ?? '')); if ($subject === '') { $this->error('请填写主题'); } if ($content === '') { $this->error('请填写内容'); } $senderId = (int)$this->auth->id; if ($senderId <= 0 || !$this->adminExists($senderId)) { $this->error('当前登录用户无效'); } $recipientIds = $this->parseIdList($params['recipient_ids'] ?? ''); if ($recipientIds === []) { $this->error('请选择投递接收人'); } $creatorId = (int)$this->auth->id; $now = time(); Db::startTrans(); try { $contentId = (int)Db::table('purchase_content')->insertGetId([ 'creator_id' => $creatorId, 'sender_id' => $senderId, 'subject' => $subject, 'content' => $content, 'createtime' => $now, 'updatetime' => $now, ]); if ($contentId <= 0) { throw new Exception('保存公告失败'); } $recvRows = []; foreach ($recipientIds as $aid) { $recvRows[] = [ 'content_id' => $contentId, 'admin_id' => $aid, 'createtime' => $now, ]; } Db::table('purchase_content_recipient')->insertAll($recvRows); Db::commit(); } catch (ValidateException|PDOException|Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success(); } public function detail($ids = null) { $row = $this->getContentRowForAdmin((int)$ids); if (!$row) { $this->error(__('No Results were found')); } $adminId = (int)$this->auth->id; if (!$this->auth->isSuperAdmin() && (int)$row['creator_id'] !== $adminId) { $isRecv = Db::table('purchase_content_recipient') ->where('content_id', (int)$row['id']) ->where('admin_id', $adminId) ->count(); if (!$isRecv) { $this->error('无权查看该通知'); } $recvRow = Db::table('purchase_content_recipient') ->where('content_id', (int)$row['id']) ->where('admin_id', $adminId) ->find(); if ($recvRow && empty($recvRow['read_time'])) { Db::table('purchase_content_recipient') ->where('id', (int)$recvRow['id']) ->update(['read_time' => time()]); } } $items = [$row]; $this->enrichContentRows($items, $adminId); $row = $items[0]; $this->view->assign('row', $row); return $this->view->fetch(); } public function del($ids = null) { if (!$this->request->isPost()) { $this->error(__('Invalid parameters')); } $ids = $ids ?: $this->request->post('ids'); $idList = $this->parseIdList($ids); if ($idList === []) { $this->error(__('Parameter %s can not be empty', 'ids')); } $adminId = (int)$this->auth->id; $isSuper = $this->auth->isSuperAdmin(); Db::startTrans(); try { foreach ($idList as $cid) { $row = $this->model->get($cid); if (!$row) { continue; } if (!$isSuper && (int)$row['creator_id'] !== $adminId) { throw new Exception('仅创建人或超级管理员可删除'); } Db::table('purchase_content_recipient')->where('content_id', $cid)->delete(); $row->delete(); } Db::commit(); } catch (PDOException|Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success(); } /** * 安装菜单(超级管理员执行一次) */ public function install() { if (!$this->auth->isSuperAdmin()) { $this->error('仅超级管理员可执行'); } $t = time(); $pid = 0; $parent = Db::name('auth_rule')->where('name', 'procuremenroot')->find(); if ($parent) { $pid = (int)$parent['id']; } else { $pick = Db::name('auth_rule')->where('name', 'procuremen/pick')->find(); if ($pick) { $pid = (int)($pick['pid'] ?? 0); } } if ($pid <= 0) { $this->error('未找到协助采购父菜单,请先在权限规则中配置 procuremenroot 或 procuremen/pick'); } $added = 0; $menuName = 'purchasecontent/index'; $menuRule = Db::name('auth_rule')->where('name', $menuName)->find(); if ($menuRule) { $menuId = (int)$menuRule['id']; } else { $menuId = Db::name('auth_rule')->insertGetId([ 'type' => 'file', 'pid' => $pid, 'name' => $menuName, 'title' => '通知公告', 'icon' => 'fa fa-bullhorn', 'url' => '', 'ismenu' => 1, 'menutype' => 'addtabs', 'weigh' => 84, 'status' => 'normal', 'createtime' => $t, 'updatetime' => $t, ]); $added++; } foreach (['purchasecontent/add' => '新增', 'purchasecontent/detail' => '查看', 'purchasecontent/del' => '删除', 'purchasecontent/adminselect' => '选择用户'] as $name => $title) { if (Db::name('auth_rule')->where('name', $name)->find()) { continue; } Db::name('auth_rule')->insert([ 'type' => 'file', 'pid' => $menuId, 'name' => $name, 'title' => $title, 'icon' => 'fa fa-circle-o', 'ismenu' => 0, 'status' => 'normal', 'createtime' => $t, 'updatetime' => $t, ]); $added++; } \think\Cache::rm('__menu__'); $this->success('通知公告菜单安装完成,新增节点 ' . $added . ' 个。请刷新后台并为角色勾选权限。'); } /** * @param array $rows */ protected function enrichContentRows(array &$rows, int $currentAdminId): void { if ($rows === []) { return; } $contentIds = []; $adminIds = []; foreach ($rows as $r) { if (!is_array($r) && !is_object($r)) { continue; } $arr = is_array($r) ? $r : $r->toArray(); $cid = (int)($arr['id'] ?? 0); if ($cid > 0) { $contentIds[$cid] = true; } foreach (['creator_id', 'sender_id'] as $fk) { $aid = (int)($arr[$fk] ?? 0); if ($aid > 0) { $adminIds[$aid] = true; } } } $nameMap = $this->loadAdminNicknameMap(array_keys($adminIds)); $recvMap = $this->loadRecipientMap(array_keys($contentIds)); foreach ($rows as &$row) { if (is_object($row) && method_exists($row, 'getData')) { $data = $row->getData(); } elseif (is_object($row) && method_exists($row, 'toArray')) { $data = $row->toArray(); } else { $data = (array)$row; } $cid = (int)($data['id'] ?? 0); $creatorId = (int)($data['creator_id'] ?? 0); $senderId = (int)($data['sender_id'] ?? 0); $recv = $recvMap[$cid] ?? ['names' => [], 'count' => 0, 'is_receiver' => false, 'read_time' => null]; $extra = [ 'creator_name' => $nameMap[$creatorId] ?? '', 'sender_name' => $nameMap[$senderId] ?? '', 'recipient_names' => implode('、', $recv['names']), 'recipient_count' => $recv['count'], 'is_receiver' => $recv['is_receiver'] ? 1 : 0, 'read_time' => $recv['read_time'], 'can_delete' => ($this->auth->isSuperAdmin() || $creatorId === $currentAdminId) ? 1 : 0, ]; if (is_object($row)) { foreach ($extra as $k => $v) { $row->$k = $v; } } else { $row = array_merge($data, $extra); } } unset($row); } /** * @param int[] $adminIds * @return array */ protected function loadAdminNicknameMap(array $adminIds): array { $adminIds = array_values(array_unique(array_filter(array_map('intval', $adminIds)))); if ($adminIds === []) { return []; } $rows = Db::name('admin') ->where('id', 'in', $adminIds) ->field('id,nickname,username') ->select(); if (!is_array($rows)) { return []; } $out = []; foreach ($rows as $r) { if (!is_array($r)) { continue; } $id = (int)($r['id'] ?? 0); $nick = trim((string)($r['nickname'] ?? '')); if ($nick === '') { $nick = trim((string)($r['username'] ?? '')); } $out[$id] = $nick !== '' ? $nick : ('#' . $id); } return $out; } /** * @param int[] $contentIds * @return array */ protected function loadRecipientMap(array $contentIds): array { $contentIds = array_values(array_unique(array_filter(array_map('intval', $contentIds)))); $out = []; if ($contentIds === []) { return $out; } $recvRows = Db::table('purchase_content_recipient') ->where('content_id', 'in', $contentIds) ->field('content_id,admin_id,read_time') ->select(); if (!is_array($recvRows)) { return $out; } $adminIds = []; foreach ($recvRows as $r) { if (!is_array($r)) { continue; } $aid = (int)($r['admin_id'] ?? 0); if ($aid > 0) { $adminIds[$aid] = true; } } $nameMap = $this->loadAdminNicknameMap(array_keys($adminIds)); $currentAdminId = (int)$this->auth->id; foreach ($recvRows as $r) { if (!is_array($r)) { continue; } $cid = (int)($r['content_id'] ?? 0); $aid = (int)($r['admin_id'] ?? 0); if ($cid <= 0) { continue; } if (!isset($out[$cid])) { $out[$cid] = ['names' => [], 'count' => 0, 'is_receiver' => false, 'read_time' => null]; } $nm = $nameMap[$aid] ?? ('#' . $aid); if ($nm !== '' && !in_array($nm, $out[$cid]['names'], true)) { $out[$cid]['names'][] = $nm; } if ($aid === $currentAdminId) { $out[$cid]['is_receiver'] = true; $rt = (int)($r['read_time'] ?? 0); if ($rt > 0) { $out[$cid]['read_time'] = $rt; } } } foreach ($out as $cid => &$item) { sort($item['names'], SORT_STRING); $item['count'] = count($item['names']); } unset($item); return $out; } protected function getContentRowForAdmin(int $id): ?array { if ($id <= 0) { return null; } $row = $this->model->get($id); return $row ? $row->toArray() : null; } protected function adminExists(int $adminId): bool { if ($adminId <= 0) { return false; } return (bool)Db::name('admin')->where('id', $adminId)->where('status', 'normal')->count(); } /** * 新增公告:可选接收人列表 * * @return array */ protected function loadNormalAdminList(): array { try { $rows = Db::name('admin') ->where('status', 'normal') ->field('id,username,nickname') ->order('id', 'asc') ->select(); } catch (\Throwable $e) { $rows = []; } if (!is_array($rows)) { return []; } $out = []; foreach ($rows as $r) { if (!is_array($r)) { continue; } $id = (int)($r['id'] ?? 0); if ($id <= 0) { continue; } $username = trim((string)($r['username'] ?? '')); $nickname = trim((string)($r['nickname'] ?? '')); if ($nickname === '') { $nickname = $username; } $label = $nickname; if ($username !== '' && $username !== $nickname) { $label .= '(' . $username . ')'; } $out[] = [ 'id' => $id, 'username' => $username, 'nickname' => $nickname, 'label' => $label, ]; } return $out; } /** * @param mixed $raw * @return int[] */ protected function parseIdList($raw): array { if (is_array($raw)) { $parts = $raw; } else { $parts = preg_split('/\s*,\s*/', trim((string)$raw), -1, PREG_SPLIT_NO_EMPTY); } if (!is_array($parts)) { return []; } $out = []; foreach ($parts as $p) { $id = (int)$p; if ($id > 0 && $this->adminExists($id)) { $out[$id] = $id; } } return array_values($out); } }