|
|
@@ -3,6 +3,7 @@
|
|
|
namespace app\admin\controller;
|
|
|
|
|
|
use app\common\controller\Backend;
|
|
|
+use think\Cache;
|
|
|
use think\Db;
|
|
|
use think\exception\PDOException;
|
|
|
use think\exception\ValidateException;
|
|
|
@@ -15,6 +16,15 @@ use Exception;
|
|
|
*/
|
|
|
class Purchasecontent extends Backend
|
|
|
{
|
|
|
+ /** 投递接收人可选范围:auth_group 根组 id(含其全部子组) */
|
|
|
+ protected const RECIPIENT_AUTH_GROUP_ROOT_ID = 10;
|
|
|
+
|
|
|
+ /** 表结构已就绪缓存键(避免每次请求重复 SHOW COLUMNS / 探表) */
|
|
|
+ protected const SCHEMA_CACHE_KEY = 'purchase_content_schema_v2';
|
|
|
+
|
|
|
+ /** 可选接收人列表缓存秒数 */
|
|
|
+ protected const RECIPIENT_LIST_CACHE_TTL = 300;
|
|
|
+
|
|
|
/** @var \app\admin\model\Purchasecontent */
|
|
|
protected $model = null;
|
|
|
|
|
|
@@ -26,7 +36,17 @@ class Purchasecontent extends Backend
|
|
|
{
|
|
|
parent::_initialize();
|
|
|
$this->model = new \app\admin\model\Purchasecontent;
|
|
|
+ if (!Cache::get(self::SCHEMA_CACHE_KEY)) {
|
|
|
+ $this->ensurePurchaseContentSchemaOnce();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 首次访问时建表/迁移,完成后写入缓存 */
|
|
|
+ protected function ensurePurchaseContentSchemaOnce(): void
|
|
|
+ {
|
|
|
$this->ensurePurchaseContentTables();
|
|
|
+ $this->ensurePurchaseContentDatetimeColumns();
|
|
|
+ Cache::set(self::SCHEMA_CACHE_KEY, 1);
|
|
|
}
|
|
|
|
|
|
protected function ensurePurchaseContentTables(): void
|
|
|
@@ -54,6 +74,68 @@ class Purchasecontent extends Backend
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /** 旧版 int 时间戳列迁移为 datetime */
|
|
|
+ protected function ensurePurchaseContentDatetimeColumns(): void
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $this->migrateTableDatetimeColumn('purchase_content', 'createtime', '投递时间');
|
|
|
+ $this->migrateTableDatetimeColumn('purchase_content', 'updatetime', '更新时间');
|
|
|
+ $this->migrateTableDatetimeColumn('purchase_content_recipient', 'createtime', '创建时间');
|
|
|
+ $this->migrateTableDatetimeColumn('purchase_content_recipient', 'read_time', '首次阅读时间');
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function migrateTableDatetimeColumn(string $table, string $column, string $comment): void
|
|
|
+ {
|
|
|
+ $rows = Db::query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
|
|
|
+ if (!is_array($rows) || !isset($rows[0]['Type'])) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $type = strtolower((string)$rows[0]['Type']);
|
|
|
+ if (strpos($type, 'int') === false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $tmp = $column . '_dt';
|
|
|
+ Db::execute("ALTER TABLE `{$table}` ADD COLUMN `{$tmp}` datetime DEFAULT NULL COMMENT '{$comment}'");
|
|
|
+ Db::execute("UPDATE `{$table}` SET `{$tmp}` = IF(`{$column}` > 0, FROM_UNIXTIME(`{$column}`), NULL)");
|
|
|
+ Db::execute("ALTER TABLE `{$table}` DROP COLUMN `{$column}`");
|
|
|
+ Db::execute("ALTER TABLE `{$table}` CHANGE `{$tmp}` `{$column}` datetime DEFAULT NULL COMMENT '{$comment}'");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 投递/阅读时间展示:YYYY-MM-DD HH:mm:ss
|
|
|
+ *
|
|
|
+ * @param mixed $value
|
|
|
+ */
|
|
|
+ protected function formatPurchaseContentTime($value): string
|
|
|
+ {
|
|
|
+ if ($value === null || $value === '') {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if (is_numeric($value)) {
|
|
|
+ $ts = (int)$value;
|
|
|
+ if ($ts > 946684800) {
|
|
|
+ return date('Y-m-d H:i:s', $ts);
|
|
|
+ }
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ $s = trim((string)$value);
|
|
|
+ if ($s === '' || stripos($s, '0000-00-00') === 0) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if (preg_match('/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/', $s, $m)) {
|
|
|
+ return $m[1];
|
|
|
+ }
|
|
|
+ if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $s, $m)) {
|
|
|
+ return $m[1] . ' 00:00:00';
|
|
|
+ }
|
|
|
+ $ts = strtotime($s);
|
|
|
+
|
|
|
+ return ($ts !== false && $ts > 0) ? date('Y-m-d H:i:s', $ts) : $s;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 选择后台用户(发送人 / 接收人)
|
|
|
*/
|
|
|
@@ -62,7 +144,15 @@ class Purchasecontent extends Backend
|
|
|
$this->model = model('Admin');
|
|
|
$this->selectpageFields = 'id,username,nickname';
|
|
|
$this->searchFields = 'id,username,nickname';
|
|
|
+ $groupIds = $this->loadRecipientAuthGroupIds();
|
|
|
$this->model->where('status', 'normal');
|
|
|
+ if ($groupIds !== []) {
|
|
|
+ $this->model->where('id', 'in', function ($query) use ($groupIds) {
|
|
|
+ $query->name('auth_group_access')->where('group_id', 'in', $groupIds)->field('uid');
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ $this->model->where('id', 0);
|
|
|
+ }
|
|
|
|
|
|
return $this->selectpage();
|
|
|
}
|
|
|
@@ -133,13 +223,13 @@ class Purchasecontent extends Backend
|
|
|
$this->error('当前登录用户无效');
|
|
|
}
|
|
|
|
|
|
- $recipientIds = $this->parseIdList($params['recipient_ids'] ?? '');
|
|
|
+ $recipientIds = $this->parseRecipientIdList($params['recipient_ids'] ?? '');
|
|
|
if ($recipientIds === []) {
|
|
|
$this->error('请选择投递接收人');
|
|
|
}
|
|
|
|
|
|
$creatorId = (int)$this->auth->id;
|
|
|
- $now = time();
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
@@ -195,7 +285,7 @@ class Purchasecontent extends Backend
|
|
|
if ($recvRow && empty($recvRow['read_time'])) {
|
|
|
Db::table('purchase_content_recipient')
|
|
|
->where('id', (int)$recvRow['id'])
|
|
|
- ->update(['read_time' => time()]);
|
|
|
+ ->update(['read_time' => date('Y-m-d H:i:s')]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -220,7 +310,6 @@ class Purchasecontent extends Backend
|
|
|
}
|
|
|
|
|
|
$adminId = (int)$this->auth->id;
|
|
|
- $isSuper = $this->auth->isSuperAdmin();
|
|
|
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
@@ -229,8 +318,8 @@ class Purchasecontent extends Backend
|
|
|
if (!$row) {
|
|
|
continue;
|
|
|
}
|
|
|
- if (!$isSuper && (int)$row['creator_id'] !== $adminId) {
|
|
|
- throw new Exception('仅创建人或超级管理员可删除');
|
|
|
+ if ((int)$row['sender_id'] !== $adminId) {
|
|
|
+ throw new Exception('仅可删除本人投递的公告');
|
|
|
}
|
|
|
Db::table('purchase_content_recipient')->where('content_id', $cid)->delete();
|
|
|
$row->delete();
|
|
|
@@ -353,14 +442,18 @@ class Purchasecontent extends Backend
|
|
|
$creatorId = (int)($data['creator_id'] ?? 0);
|
|
|
$senderId = (int)($data['sender_id'] ?? 0);
|
|
|
$recv = $recvMap[$cid] ?? ['names' => [], 'count' => 0, 'is_receiver' => false, 'read_time' => null];
|
|
|
+ $isSender = ($senderId === $currentAdminId);
|
|
|
$extra = [
|
|
|
'creator_name' => $nameMap[$creatorId] ?? '',
|
|
|
'sender_name' => $nameMap[$senderId] ?? '',
|
|
|
+ 'sender_id' => $senderId,
|
|
|
'recipient_names' => implode('、', $recv['names']),
|
|
|
'recipient_count' => $recv['count'],
|
|
|
'is_receiver' => $recv['is_receiver'] ? 1 : 0,
|
|
|
+ 'is_sender' => $isSender ? 1 : 0,
|
|
|
'read_time' => $recv['read_time'],
|
|
|
- 'can_delete' => ($this->auth->isSuperAdmin() || $creatorId === $currentAdminId) ? 1 : 0,
|
|
|
+ 'send_time_text' => $this->formatPurchaseContentTime($data['createtime'] ?? ''),
|
|
|
+ 'can_delete' => $isSender ? 1 : 0,
|
|
|
];
|
|
|
if (is_object($row)) {
|
|
|
foreach ($extra as $k => $v) {
|
|
|
@@ -455,9 +548,9 @@ class Purchasecontent extends Backend
|
|
|
}
|
|
|
if ($aid === $currentAdminId) {
|
|
|
$out[$cid]['is_receiver'] = true;
|
|
|
- $rt = (int)($r['read_time'] ?? 0);
|
|
|
- if ($rt > 0) {
|
|
|
- $out[$cid]['read_time'] = $rt;
|
|
|
+ $rt = $r['read_time'] ?? null;
|
|
|
+ if ($rt !== null && $rt !== '' && stripos((string)$rt, '0000-00-00') !== 0) {
|
|
|
+ $out[$cid]['read_time'] = $this->formatPurchaseContentTime($rt);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -490,15 +583,110 @@ class Purchasecontent extends Backend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 新增公告:可选接收人列表
|
|
|
+ * 接收人是否属于「供应商证」角色组(id=10)及其子组
|
|
|
+ */
|
|
|
+ protected function recipientAdminExists(int $adminId): bool
|
|
|
+ {
|
|
|
+ if ($adminId <= 0 || !$this->adminExists($adminId)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $groupIds = $this->loadRecipientAuthGroupIds();
|
|
|
+ if ($groupIds === []) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (bool)Db::name('auth_group_access')
|
|
|
+ ->where('uid', $adminId)
|
|
|
+ ->where('group_id', 'in', $groupIds)
|
|
|
+ ->count();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * auth_group id=10 及其全部子组 id
|
|
|
+ *
|
|
|
+ * @return int[]
|
|
|
+ */
|
|
|
+ protected function loadRecipientAuthGroupIds(): array
|
|
|
+ {
|
|
|
+ static $memo = null;
|
|
|
+ if ($memo !== null) {
|
|
|
+ return $memo;
|
|
|
+ }
|
|
|
+ $cacheKey = 'purchase_content_group_ids_' . self::RECIPIENT_AUTH_GROUP_ROOT_ID;
|
|
|
+ $cached = Cache::get($cacheKey);
|
|
|
+ if (is_array($cached) && $cached !== []) {
|
|
|
+ $memo = $cached;
|
|
|
+
|
|
|
+ return $memo;
|
|
|
+ }
|
|
|
+
|
|
|
+ $rootId = self::RECIPIENT_AUTH_GROUP_ROOT_ID;
|
|
|
+ try {
|
|
|
+ $groups = Db::name('auth_group')->where('status', 'normal')->field('id,pid')->select();
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ return [$rootId];
|
|
|
+ }
|
|
|
+ if (!is_array($groups) || $groups === []) {
|
|
|
+ return [$rootId];
|
|
|
+ }
|
|
|
+ $groupList = [];
|
|
|
+ foreach ($groups as $g) {
|
|
|
+ if (is_array($g)) {
|
|
|
+ $groupList[] = $g;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($groupList === []) {
|
|
|
+ return [$rootId];
|
|
|
+ }
|
|
|
+ $tree = \fast\Tree::instance();
|
|
|
+ $tree->init($groupList);
|
|
|
+ $ids = $tree->getChildrenIds($rootId, true);
|
|
|
+ if (!is_array($ids) || $ids === []) {
|
|
|
+ $memo = [$rootId];
|
|
|
+
|
|
|
+ return $memo;
|
|
|
+ }
|
|
|
+
|
|
|
+ $memo = array_values(array_unique(array_filter(array_map('intval', $ids))));
|
|
|
+ Cache::set($cacheKey, $memo, self::RECIPIENT_LIST_CACHE_TTL);
|
|
|
+
|
|
|
+ return $memo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增公告:可选接收人列表(仅角色组 id=10 及其子组下的用户)
|
|
|
*
|
|
|
* @return array<int, array{id:int, username:string, nickname:string, label:string}>
|
|
|
*/
|
|
|
protected function loadNormalAdminList(): array
|
|
|
{
|
|
|
+ $cacheKey = 'purchase_content_admin_list_v2_' . self::RECIPIENT_AUTH_GROUP_ROOT_ID;
|
|
|
+ $cached = Cache::get($cacheKey);
|
|
|
+ if (is_array($cached)) {
|
|
|
+ return $cached;
|
|
|
+ }
|
|
|
+
|
|
|
+ $groupIds = $this->loadRecipientAuthGroupIds();
|
|
|
+ if ($groupIds === []) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $adminIds = Db::name('auth_group_access')
|
|
|
+ ->where('group_id', 'in', $groupIds)
|
|
|
+ ->column('uid');
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ $adminIds = [];
|
|
|
+ }
|
|
|
+ $adminIds = is_array($adminIds)
|
|
|
+ ? array_values(array_unique(array_filter(array_map('intval', $adminIds))))
|
|
|
+ : [];
|
|
|
+ if ($adminIds === []) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
try {
|
|
|
$rows = Db::name('admin')
|
|
|
->where('status', 'normal')
|
|
|
+ ->where('id', 'in', $adminIds)
|
|
|
->field('id,username,nickname')
|
|
|
->order('id', 'asc')
|
|
|
->select();
|
|
|
@@ -522,18 +710,19 @@ class Purchasecontent extends Backend
|
|
|
if ($nickname === '') {
|
|
|
$nickname = $username;
|
|
|
}
|
|
|
- $label = $nickname;
|
|
|
- if ($username !== '' && $username !== $nickname) {
|
|
|
- $label .= '(' . $username . ')';
|
|
|
+ if ($nickname === '') {
|
|
|
+ $nickname = '#' . $id;
|
|
|
}
|
|
|
$out[] = [
|
|
|
'id' => $id,
|
|
|
'username' => $username,
|
|
|
'nickname' => $nickname,
|
|
|
- 'label' => $label,
|
|
|
+ 'label' => $nickname,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
+ Cache::set($cacheKey, $out, self::RECIPIENT_LIST_CACHE_TTL);
|
|
|
+
|
|
|
return $out;
|
|
|
}
|
|
|
|
|
|
@@ -554,7 +743,25 @@ class Purchasecontent extends Backend
|
|
|
$out = [];
|
|
|
foreach ($parts as $p) {
|
|
|
$id = (int)$p;
|
|
|
- if ($id > 0 && $this->adminExists($id)) {
|
|
|
+ if ($id > 0) {
|
|
|
+ $out[$id] = $id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return array_values($out);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析并校验接收人 id(须为角色组 id=10 及其子组下的正常用户)
|
|
|
+ *
|
|
|
+ * @param mixed $raw
|
|
|
+ * @return int[]
|
|
|
+ */
|
|
|
+ protected function parseRecipientIdList($raw): array
|
|
|
+ {
|
|
|
+ $out = [];
|
|
|
+ foreach ($this->parseIdList($raw) as $id) {
|
|
|
+ if ($this->recipientAdminExists($id)) {
|
|
|
$out[$id] = $id;
|
|
|
}
|
|
|
}
|