From bf8216892712cb8135febf16a2c22a69a4b11684 Mon Sep 17 00:00:00 2001 From: augushong Date: Mon, 12 Oct 2026 12:09:01 +0000 Subject: [PATCH] =?UTF-8?q?feat(notice):=20=E9=80=9A=E7=9F=A5=E7=AE=A1?= =?UTF-8?q?=E7=90=86=20CURD=20=E6=A8=A1=E5=9D=97=E5=8F=8C=E5=B1=82?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/controller/system/Notice.php | 14 ++ .../admin/controller/system/NoticeBase.php | 185 ++++++++++++++++++ .../base/admin/view/system/notice/_common.js | 11 ++ .../base/admin/view/system/notice/index.html | 7 + extend/base/admin/view/system/notice/index.js | 54 +++++ .../base/admin/view/system/notice/read.html | 119 +++++++++++ extend/base/admin/view/system/notice/read.js | 3 + 7 files changed, 393 insertions(+) create mode 100644 app/admin/controller/system/Notice.php create mode 100644 extend/base/admin/controller/system/NoticeBase.php create mode 100644 extend/base/admin/view/system/notice/_common.js create mode 100644 extend/base/admin/view/system/notice/index.html create mode 100644 extend/base/admin/view/system/notice/index.js create mode 100644 extend/base/admin/view/system/notice/read.html create mode 100644 extend/base/admin/view/system/notice/read.js diff --git a/app/admin/controller/system/Notice.php b/app/admin/controller/system/Notice.php new file mode 100644 index 0000000..c01299e --- /dev/null +++ b/app/admin/controller/system/Notice.php @@ -0,0 +1,14 @@ + 'desc', + ]; + + /** + * 行内属性修改允许的字段(通知主体由系统流程生成,暂不开放字段; + * 保留标准白名单结构,下游可按需放开). + * @var array + */ + protected $allowModifyFields = []; + + public function __construct(App $app) + { + parent::__construct($app); + + $this->model = new SystemNotice(); + + // 场景标题映射(scene 等值搜索下拉与列表徽章共用;data_brage 通道下发) + $sceneOptions = []; + foreach (NoticeService::scenes() as $scene => $item) { + $sceneOptions[$scene] = $item['title']; + } + $this->assign('select_list_scene', $sceneOptions, true); + } + + /** + * @NodeAnotation(title="列表") + */ + public function index() + { + if ($this->request->isAjax()) { + if (input('selectFields')) { + return $this->selectList(); + } + list($page, $limit, $where) = $this->buildTableParames(); + $count = $this->model + ->where($where) + ->count(); + $list = $this->model + ->where($where) + ->page($page, $limit) + ->order($this->sort) + ->select(); + + // 发送者昵称:system_admin 与主表存在同名字段,禁用 withJoin 预载; + // 单查昵称缓存后循环附加 + $senderIds = array_values(array_filter(array_unique($list->column('send_admin_id')))); + $nicknames = []; + if (!empty($senderIds)) { + $nicknames = SystemAdmin::whereIn('id', $senderIds)->column('nickname', 'id'); + } + foreach ($list as $row) { + $row['send_admin_nickname'] = $nicknames[$row['send_admin_id']] ?? ''; + } + + $data = [ + 'code' => 0, + 'msg' => '', + 'count' => $count, + 'data' => $list, + ]; + + return json($data); + } + + return $this->fetch(); + } + + /** + * @NodeAnotation(title="详情") + */ + public function read($id) + { + $row = $this->model->find($id); + empty($row) && $this->error('数据不存在'); + + // 发送者昵称(单查,勿 withJoin,同列表口径) + $senderNickname = ''; + if (!empty($row->send_admin_id)) { + $senderNickname = SystemAdmin::where('id', $row->send_admin_id)->value('nickname') ?: ''; + } + + // 投递汇总:channel × status 聚合计数(一条 group 查询) + $groups = SystemNoticeTarget::where('notice_id', $id) + ->field('channel,status,count(*) as cnt') + ->group('channel,status') + ->select(); + $statusCount = []; + foreach ($groups as $group) { + $statusCount[$group['channel']][$group['status']] = (int) $group['cnt']; + } + + $channelNames = [ + NoticeService::CHANNEL_INMAIL => '站内信', + NoticeService::CHANNEL_EMAIL => '邮件', + NoticeService::CHANNEL_SMS => '短信', + NoticeService::CHANNEL_WECHAT => '公众号', + ]; + $delivery = []; + foreach ($statusCount as $channel => $counts) { + $delivery[] = [ + 'channel' => $channel, + 'channel_name' => $channelNames[$channel] ?? $channel, + 'pending' => $counts[SystemNoticeTarget::STATUS_PENDING] ?? 0, + 'sending' => $counts[SystemNoticeTarget::STATUS_SENDING] ?? 0, + 'success' => $counts[SystemNoticeTarget::STATUS_SUCCESS] ?? 0, + 'failed' => $counts[SystemNoticeTarget::STATUS_FAILED] ?? 0, + ]; + } + + // 场景标题(scenes() 动态映射,未注册场景回退原值) + $sceneTitle = NoticeService::scenes()[$row->scene]['title'] ?? $row->scene; + + $this->assign('row', $row); + $this->assign('title', $row->title); + $this->assign('sender_nickname', $senderNickname); + $this->assign('scene_title', $sceneTitle); + $this->assign('delivery', $delivery); + + return $this->fetch(); + } + + /** + * @NodeAnotation(title="删除") + */ + public function delete($id) + { + $this->checkPostRequest(); + $row = $this->model->whereIn('id', $id)->select(); + $row->isEmpty() && $this->error('数据不存在'); + try { + // 模型启用软删(delete_time),delete 即软删;投递行保留不级联 + $save = $row->delete(); + } catch (\Exception $e) { + $this->error('删除失败:' . $e->getMessage()); + } + $save ? $this->success('删除成功') : $this->error('删除失败'); + } + + /** + * @NodeAnotation(title="属性修改") + */ + public function modify() + { + $this->checkPostRequest(); + $post = $this->request->post(); + $rule = [ + 'id|ID' => 'require', + 'field|字段' => 'require', + 'value|值' => 'require', + ]; + $this->validate($post, $rule); + if (!in_array($post['field'], $this->allowModifyFields)) { + $this->error('该字段不允许修改:' . $post['field']); + } + $row = $this->model->find($post['id']); + empty($row) && $this->error('数据不存在'); + try { + $save = $row->save([$post['field'] => $post['value']]); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $save ? $this->success('保存成功') : $this->error('未做变更'); + } +} diff --git a/extend/base/admin/view/system/notice/_common.js b/extend/base/admin/view/system/notice/_common.js new file mode 100644 index 0000000..f0e296c --- /dev/null +++ b/extend/base/admin/view/system/notice/_common.js @@ -0,0 +1,11 @@ +var init = { + tableElem: '#currentTable', + tableRenderId: 'currentTableRenderId', + indexUrl: 'system.notice/index', + addUrl: '', + editUrl: '', + readUrl: 'system.notice/read', + deleteUrl: 'system.notice/delete', + exportUrl: '', + modifyUrl: 'system.notice/modify', +}; diff --git a/extend/base/admin/view/system/notice/index.html b/extend/base/admin/view/system/notice/index.html new file mode 100644 index 0000000..9475165 --- /dev/null +++ b/extend/base/admin/view/system/notice/index.html @@ -0,0 +1,7 @@ +
+
+ +
+
+
diff --git a/extend/base/admin/view/system/notice/index.js b/extend/base/admin/view/system/notice/index.js new file mode 100644 index 0000000..c0fef13 --- /dev/null +++ b/extend/base/admin/view/system/notice/index.js @@ -0,0 +1,54 @@ +$(function(){ + // 场景徽章:标题取 data_brage 下发的 scenes() 动态映射,色标按场景写死(layui-badge 色系) + var sceneColorMap = { + system: 'layui-badge', + common: 'layui-badge layui-bg-blue', + admin_login: 'layui-badge layui-bg-green', + mall_stock: 'layui-badge layui-bg-orange', + }; + + var sceneTemplet = function(d) { + var titles = ua.getDataBrage('select_list_scene'); + var text = titles[d.scene] || d.scene; + var cls = sceneColorMap[d.scene] || 'layui-badge layui-bg-gray'; + return '' + text + ''; + }; + + var senderTemplet = function(d) { + if (!d.send_admin_id) { + return '系统'; + } + return d.send_admin_nickname || ('#' + d.send_admin_id); + }; + + ua.table.render({ + init: init, + toolbar: ['refresh', 'delete'], + defaultToolbar: ['filter'], + cols: [[ + {type: 'checkbox'}, + {field: 'id', title: 'ID', width: 80, sort: true}, + {field: 'scene', title: '场景', width: 110, search: 'select', searchOp: '=', selectList: ua.getDataBrage('select_list_scene'), templet: sceneTemplet}, + {field: 'title', title: '标题', minWidth: 220}, + {field: 'send_admin_nickname', title: '发送者', width: 120, search: false, templet: senderTemplet}, + {field: 'create_time', title: '创建时间', width: 170, templet: ua.table.date, search: 'range'}, + { + width: 130, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [ + [{ + class: 'layui-btn layui-btn-primary layui-btn-xs', + method: 'tab', + field: 'id', + text: '详情', + title: '查看详情', + auth: 'read', + url: init.readUrl, + icon: '' + }], + 'delete' + ] + }, + ]], + }); + + ua.listen(); +}) diff --git a/extend/base/admin/view/system/notice/read.html b/extend/base/admin/view/system/notice/read.html new file mode 100644 index 0000000..e464b2c --- /dev/null +++ b/extend/base/admin/view/system/notice/read.html @@ -0,0 +1,119 @@ +
+
+
+
+
+
+

#{$row.id} {$title}

+
ID: {$row.id}
+
+
+ +
+
+
+
+
+ +
+
+
+
场景
+
+ {switch name="row.scene"} + {case value="system"}{$scene_title}{/case} + {case value="common"}{$scene_title}{/case} + {case value="admin_login"}{$scene_title}{/case} + {case value="mall_stock"}{$scene_title}{/case} + {default /}{$scene_title} + {/switch} +
+
+
+
标题
+
+ {notempty name="row.title"} + {$row.title} + {else/} + 暂无数据 + {/notempty} +
+
+
+
内容
+
+ {notempty name="row.content"} + {$row.content|raw} + {else/} + 暂无内容 + {/notempty} +
+
+
+
业务关联ID
+
+ {notempty name="row.biz_id"} + {$row.biz_id} + {else/} + - + {/notempty} +
+
+ +
+
投递汇总
+
+ {notempty name="delivery"} + {volist name="delivery" id="vo"} +
+ {$vo.channel_name} + 成功 {$vo.success} + 失败 {$vo.failed} + 待发 {$vo.pending} + {if condition="$vo.sending gt 0"}发送中 {$vo.sending}{/if} +
+ {/volist} + {else/} + 暂无投递记录 + {/notempty} +
+
+ +
+
+ +
+

基础信息

+
+
+
ID
+
{$row.id}
+
+
+
发送者
+
+ {if condition="$row.send_admin_id gt 0"} + {$sender_nickname|default=''}(#{$row.send_admin_id}) + {else/} + 系统 + {/if} +
+
+
+
创建时间
+
+ {notempty name="row.create_time"} + {$row.create_time|date="Y-m-d H:i:s"} + {else/} + 暂无数据 + {/notempty} +
+
+ +
+
+
+
+
+
+
diff --git a/extend/base/admin/view/system/notice/read.js b/extend/base/admin/view/system/notice/read.js new file mode 100644 index 0000000..81a35d4 --- /dev/null +++ b/extend/base/admin/view/system/notice/read.js @@ -0,0 +1,3 @@ +$(function(){ + ua.listen(); +})