Files
star-erp/app/Modules/Core/Controllers/UserController.php
sky121113 b0848a6bb8 chore: 完善模組化架構遷移與修復前端顯示錯誤
- 修正所有模組 Controller 的 Model 引用路徑 (App\Modules\...)
- 更新 ProductionOrder 與 ProductionOrderItem 模型結構以符合新版邏輯
- 修復 resources/js/utils/format.ts 在處理空值時導致 toLocaleString 崩潰的問題
- 清除全域路徑與 Controller 遷移殘留檔案
2026-01-26 10:37:47 +08:00

246 lines
7.9 KiB
PHP

<?php
namespace App\Modules\Core\Controllers;
use App\Http\Controllers\Controller;
use App\Modules\Core\Models\User;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Inertia\Inertia;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$perPage = $request->input('per_page', 10);
$sortBy = $request->input('sort_by', 'id');
$sortOrder = $request->input('sort_order', 'asc');
$search = $request->input('search');
$roleId = $request->input('role');
$query = User::with(['roles:id,name,display_name']);
// Handle Search
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%");
});
}
// Handle Role Filter
if ($roleId && $roleId !== 'all') {
$query->whereHas('roles', function ($q) use ($roleId) {
$q->where('id', $roleId);
});
}
// Handle sorting
if (in_array($sortBy, ['name', 'created_at'])) {
$query->orderBy($sortBy, $sortOrder);
} else {
$query->orderBy('id', 'asc');
}
$users = $query->paginate($perPage)->withQueryString();
$roles = Role::select('id', 'name', 'display_name')->get();
return Inertia::render('Admin/User/Index', [
'users' => $users,
'roles' => $roles,
'filters' => $request->only(['per_page', 'sort_by', 'sort_order', 'search', 'role']),
]);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$roles = Role::pluck('display_name', 'name');
return Inertia::render('Admin/User/Create', [
'roles' => $roles
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'string', 'email', 'max:255', 'unique:users'],
'username' => ['required', 'string', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'roles' => ['array'],
], [
'password.required' => '請輸入密碼',
'password.min' => '密碼長度至少需 :min 個字元',
'password.confirmed' => '密碼確認不符',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'username' => $validated['username'],
'password' => Hash::make($validated['password']),
]);
if (!empty($validated['roles'])) {
$user->syncRoles($validated['roles']);
// Update the 'created' log to include roles
$activity = \Spatie\Activitylog\Models\Activity::where('subject_type', get_class($user))
->where('subject_id', $user->id)
->where('event', 'created')
->latest()
->first();
if ($activity) {
$roleNames = $user->roles()->pluck('display_name')->join(', ');
$properties = $activity->properties->toArray();
$properties['attributes']['role_id'] = $roleNames;
$activity->properties = $properties;
$activity->save();
}
}
return redirect()->route('users.index')->with('success', '使用者建立成功');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$user = User::with('roles')->findOrFail($id);
$roles = Role::get(['id', 'name', 'display_name']);
return Inertia::render('Admin/User/Edit', [
'user' => $user,
'roles' => $roles,
'currentRoles' => $user->getRoleNames()
]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$user = User::findOrFail($id);
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
'username' => ['required', 'string', 'max:255', Rule::unique('users')->ignore($user->id)],
'password' => ['nullable', 'string', 'min:8', 'confirmed'],
'roles' => ['array'],
], [
'password.min' => '密碼長度至少需 :min 個字元',
'password.confirmed' => '密碼確認不符',
]);
// 1. Prepare data and detect changes
$userData = [
'name' => $validated['name'],
'email' => $validated['email'],
'username' => $validated['username'],
];
if (!empty($validated['password'])) {
$userData['password'] = Hash::make($validated['password']);
}
$user->fill($userData);
// Capture dirty attributes for manual logging
$dirty = $user->getDirty();
$oldAttributes = [];
$newAttributes = [];
foreach ($dirty as $key => $value) {
$oldAttributes[$key] = $user->getOriginal($key);
$newAttributes[$key] = $value;
}
// Save without triggering events (prevents duplicate log)
$user->saveQuietly();
// 2. Handle Roles
$roleChanges = null;
if (isset($validated['roles'])) {
$oldRoles = $user->roles()->pluck('display_name')->join(', ');
$user->syncRoles($validated['roles']);
$newRoles = $user->roles()->pluck('display_name')->join(', ');
if ($oldRoles !== $newRoles) {
$roleChanges = [
'old' => $oldRoles,
'new' => $newRoles
];
}
}
// 3. Manually Log activity (Single Consolidated Log)
if (!empty($newAttributes) || $roleChanges) {
$properties = [
'attributes' => $newAttributes,
'old' => $oldAttributes,
];
if ($roleChanges) {
$properties['attributes']['role_id'] = $roleChanges['new'];
$properties['old']['role_id'] = $roleChanges['old'];
}
activity()
->performedOn($user)
->causedBy(auth()->user())
->event('updated')
->withProperties($properties)
->tap(function (\Spatie\Activitylog\Contracts\Activity $activity) use ($user) {
// Manually add snapshot since we aren't using the model's LogOptions due to saveQuietly
$activity->properties = $activity->properties->merge([
'snapshot' => [
'name' => $user->name,
'username' => $user->username,
]
]);
})
->log('updated');
}
return redirect()->route('users.index')->with('success', '使用者更新成功');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$user = User::findOrFail($id);
if ($user->hasRole('super-admin')) {
return back()->with('error', '無法刪除超級管理員帳號');
}
if ($user->id === auth()->id()) {
return back()->with('error', '無法刪除自己');
}
$user->delete();
return redirect()->route('users.index')->with('success', '使用者已刪除');
}
}