Files
chatroom/database/seeders/DepartmentPositionSeeder.php
lkddi 5f30220609 feat: 任命/撤销通知系统 + 用户名片UI优化
- 任命/撤销事件增加 type 字段区分类型
- 任命:全屏礼花 + 紫色弹窗 + 紫色系统消息
- 撤销:灰色弹窗 + 灰色系统消息,无礼花
- 消息分发:操作者/被操作者显示在私聊面板,其他人显示在公屏
- 系统消息加随机鼓励语(各5条轮换)
- ChatStateService 修复 Redis key 前缀扫描问题(getAllActiveRoomIds)
- 用户名片折叠优化:管理员视野、职务履历均可折叠
- 管理操作 + 职务操作合并为「🔧 管理操作」折叠区
- 悄悄话改为「🎁 送礼物」按钮,礼物面板内联展开
2026-02-28 23:44:38 +08:00

241 lines
12 KiB
PHP
Raw Permalink 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
/**
* 文件功能:部门与职务数据填充器
* 预填所有部门(办公厅 / 迎宾部 / 聊务部 / 宣传部)
* 及各部门下的完整职务数据含图标、rank、level、人数/奖励上限)
* 并写入默认的任命权限白名单position_appoint_limits
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace Database\Seeders;
use App\Models\Department;
use App\Models\Position;
use Illuminate\Database\Seeder;
class DepartmentPositionSeeder extends Seeder
{
/**
* 执行数据填充
*/
public function run(): void
{
// ── 1. 创建部门 ────────────────────────────────────────────────────
$depts = [];
foreach ($this->departmentsData() as $data) {
$depts[$data['name']] = Department::firstOrCreate(
['name' => $data['name']],
$data
);
}
// ── 2. 创建职务 ────────────────────────────────────────────────────
$positions = []; // key = "部门名::职务名"
foreach ($this->positionsData() as $row) {
$dept = $depts[$row['department']];
$position = Position::firstOrCreate(
['department_id' => $dept->id, 'name' => $row['name']],
[
'department_id' => $dept->id,
'name' => $row['name'],
'icon' => $row['icon'],
'rank' => $row['rank'],
'level' => $row['level'],
'max_persons' => $row['max_persons'],
'max_reward' => $row['max_reward'],
'sort_order' => $row['sort_order'],
]
);
$positions["{$row['department']}::{$row['name']}"] = $position;
}
// ── 3. 设置任命权限白名单 ────────────────────────────────────────────
foreach ($this->appointLimitsData($positions) as [$appointer, $appointableList]) {
/** @var Position $appointerPosition */
$appointerPosition = $positions[$appointer] ?? null;
if (! $appointerPosition) {
continue;
}
// 同步任命白名单(不附加旧数据,完整覆盖)
$appointableIds = collect($appointableList)
->map(fn ($key) => $positions[$key] ?? null)
->filter()
->pluck('id')
->toArray();
$appointerPosition->appointablePositions()->sync($appointableIds);
}
$this->command->info('部门职务 Seeder 完成:'.count($positions).' 个职务已写入。');
}
/**
* 部门基础数据
*
* @return array<int, array<string, mixed>>
*/
private function departmentsData(): array
{
return [
['name' => '办公厅', 'rank' => 99, 'color' => '#8B0000', 'sort_order' => 1, 'description' => '站级最高指挥机构'],
['name' => '迎宾部', 'rank' => 80, 'color' => '#1a5276', 'sort_order' => 2, 'description' => '负责新用户迎接与接待工作'],
['name' => '聊务部', 'rank' => 75, 'color' => '#196F3D', 'sort_order' => 3, 'description' => '负责聊天室日常管理与氛围维护'],
['name' => '宣传部', 'rank' => 70, 'color' => '#7D6608', 'sort_order' => 4, 'description' => '负责聊天室宣传推广与活动策划'],
];
}
/**
* 职务完整数据
*
* @return array<int, array<string, mixed>>
*/
private function positionsData(): array
{
// [部门, 职务名, icon, rank, level, max_persons, max_reward, sort_order]
$rows = [
// ── 办公厅 ──────────────────────────────────────────────────────
['办公厅', '站长', '👑', 99, 100, 1, null, 1],
['办公厅', '执行站长', '⭐', 97, 99, 1, null, 2],
['办公厅', '常务副站长', '📜', 95, 98, 1, 2000, 3],
['办公厅', '副站长', '🔴', 93, 97, 1, 1500, 4],
['办公厅', '秘书长', '✏️', 91, 96, 2, 1200, 5],
['办公厅', '三部总长', '🪖', 89, 95, 1, 1000, 6],
// ── 迎宾部 ──────────────────────────────────────────────────────
['迎宾部', '迎宾部长', '🏆', 87, 94, 1, 800, 1],
['迎宾部', '迎宾政委', '👤', 87, 94, 1, 800, 2],
['迎宾部', '迎宾常务副部长', '📋', 85, 93, 1, 500, 3],
['迎宾部', '迎宾副部长', '🔵', 83, 92, 1, 300, 4],
['迎宾部', '迎宾副政委', '🔵', 81, 91, 1, 300, 5],
['迎宾部', '金牌迎宾员', '🥇', 70, 90, 2, null, 6],
['迎宾部', '银牌迎宾员', '🥈', 50, 80, 3, null, 7],
['迎宾部', '铜牌迎宾员', '🥉', 30, 70, 4, null, 8],
['迎宾部', '实习迎宾员', '🌱', 10, 60, 4, null, 9],
// ── 聊务部 ──────────────────────────────────────────────────────
['聊务部', '聊务部部长', '🏆', 87, 94, 1, 800, 1],
['聊务部', '聊务部政委', '👤', 87, 94, 1, 800, 2],
['聊务部', '聊务部常务副部长', '📋', 85, 93, 1, 500, 3],
['聊务部', '聊务部副部长', '🔵', 83, 92, 1, 300, 4],
['聊务部', '聊务部副政委', '🔵', 81, 91, 1, 300, 5],
['聊务部', '金牌聊务员', '🥇', 70, 90, 2, null, 6],
['聊务部', '银牌聊务员', '🥈', 50, 80, 3, null, 7],
['聊务部', '铜牌聊务员', '🥉', 30, 70, 4, null, 8],
['聊务部', '实习聊务员', '🌱', 10, 60, 4, null, 9],
// ── 宣传部 ──────────────────────────────────────────────────────
['宣传部', '宣传部部长', '🏆', 87, 94, 1, 800, 1],
['宣传部', '宣传部政委', '👤', 87, 94, 1, 800, 2],
['宣传部', '宣传部常务副部长', '📋', 85, 93, 1, 500, 3],
['宣传部', '宣传部副部长', '🔵', 83, 92, 1, 300, 4],
['宣传部', '宣传部副政委', '🔵', 81, 91, 1, 300, 5],
['宣传部', '金牌宣传员', '🥇', 70, 90, 2, null, 6],
['宣传部', '银牌宣传员', '🥈', 50, 80, 3, null, 7],
['宣传部', '铜牌宣传员', '🥉', 30, 70, 4, null, 8],
['宣传部', '实习宣传员', '🌱', 10, 60, 4, null, 9],
];
return array_map(fn ($r) => [
'department' => $r[0],
'name' => $r[1],
'icon' => $r[2],
'rank' => $r[3],
'level' => $r[4],
'max_persons' => $r[5],
'max_reward' => $r[6],
'sort_order' => $r[7],
], $rows);
}
/**
* 默认任命权限白名单
* key = "任命人职务对应的 positions key"
* value = [可被任命的职务 key 列表]
*
* @param array<string, Position> $positions
* @return array<int, array{0: string, 1: list<string>}>
*/
private function appointLimitsData(array $positions): array
{
// 全三部门的所有职务 key 列表(不含办公厅)
$allThreeDepts = array_keys(array_filter(
$positions,
fn ($k) => ! str_starts_with($k, '办公厅::'),
ARRAY_FILTER_USE_KEY
));
// 三部门所有部长/政委及以下(全部)
$allThreeAll = $allThreeDepts;
// 三部门常务副部长及以下level <= 93
$allThreeBelowZhangzhang = array_keys(array_filter(
$positions,
fn ($p, $k) => ! str_starts_with($k, '办公厅::') && $p->level <= 93,
ARRAY_FILTER_USE_BOTH
));
// 三部门副部长/副政委及以下level <= 92
$allThreeBelowFubu = array_keys(array_filter(
$positions,
fn ($p, $k) => ! str_starts_with($k, '办公厅::') && $p->level <= 92,
ARRAY_FILTER_USE_BOTH
));
// 三部门金牌及以下level <= 90
$allThreeBelowGold = array_keys(array_filter(
$positions,
fn ($p, $k) => ! str_starts_with($k, '办公厅::') && $p->level <= 90,
ARRAY_FILTER_USE_BOTH
));
// 三部门银牌及以下level <= 80
$allThreeBelowSilver = array_keys(array_filter(
$positions,
fn ($p, $k) => ! str_starts_with($k, '办公厅::') && $p->level <= 80,
ARRAY_FILTER_USE_BOTH
));
/**
* 便捷闭包:获取指定部门、指定最大 level 的职务 key 列表
*/
$deptBelow = fn (string $dept, int $maxLevel) => array_keys(array_filter(
$positions,
fn ($p, $k) => str_starts_with($k, "{$dept}::") && $p->level <= $maxLevel,
ARRAY_FILTER_USE_BOTH
));
return [
// 办公厅
['办公厅::站长', array_merge(
['办公厅::执行站长', '办公厅::常务副站长', '办公厅::副站长', '办公厅::秘书长', '办公厅::三部总长'],
$allThreeAll
)],
['办公厅::执行站长', $allThreeAll], // 三部所有职务
['办公厅::常务副站长', $allThreeBelowZhangzhang], // 三部常务副部长及以下
['办公厅::副站长', $allThreeBelowFubu], // 三部副部长级及以下
['办公厅::秘书长', $allThreeBelowGold], // 三部金牌及以下
['办公厅::三部总长', $allThreeBelowSilver], // 三部银牌及以下
// 迎宾部
['迎宾部::迎宾部长', $deptBelow('迎宾部', 90)],
['迎宾部::迎宾政委', $deptBelow('迎宾部', 90)],
['迎宾部::迎宾常务副部长', $deptBelow('迎宾部', 80)],
['迎宾部::迎宾副部长', $deptBelow('迎宾部', 70)],
['迎宾部::迎宾副政委', $deptBelow('迎宾部', 70)],
// 聊务部
['聊务部::聊务部部长', $deptBelow('聊务部', 90)],
['聊务部::聊务部政委', $deptBelow('聊务部', 90)],
['聊务部::聊务部常务副部长', $deptBelow('聊务部', 80)],
['聊务部::聊务部副部长', $deptBelow('聊务部', 70)],
['聊务部::聊务部副政委', $deptBelow('聊务部', 70)],
// 宣传部
['宣传部::宣传部部长', $deptBelow('宣传部', 90)],
['宣传部::宣传部政委', $deptBelow('宣传部', 90)],
['宣传部::宣传部常务副部长', $deptBelow('宣传部', 80)],
['宣传部::宣传部副部长', $deptBelow('宣传部', 70)],
['宣传部::宣传部副政委', $deptBelow('宣传部', 70)],
];
}
}