[FEAT] 優化部署流程:加入 RoleSeeder 與 AdminUserSeeder,並實作權限系統基礎架構與多租戶隔離機制
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 48s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 48s
This commit is contained in:
125
app/Http/Controllers/Admin/CompanyController.php
Normal file
125
app/Http/Controllers/Admin/CompanyController.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\System\Company;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CompanyController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Company::query()->withCount(['users', 'machines']);
|
||||
|
||||
// 搜尋
|
||||
if ($search = $request->input('search')) {
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('code', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 狀態篩選
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
$limit = $request->input('limit', 10);
|
||||
$companies = $query->latest()->paginate($limit)->withQueryString();
|
||||
|
||||
return view('admin.companies.index', compact('companies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'code' => 'required|string|max:50|unique:companies,code',
|
||||
'tax_id' => 'nullable|string|max:50',
|
||||
'contact_name' => 'nullable|string|max:255',
|
||||
'contact_phone' => 'nullable|string|max:50',
|
||||
'contact_email' => 'nullable|email|max:255',
|
||||
'valid_until' => 'nullable|date',
|
||||
'status' => 'required|boolean',
|
||||
'note' => 'nullable|string',
|
||||
// 帳號相關欄位 (可選)
|
||||
'admin_username' => 'nullable|string|max:255|unique:users,username',
|
||||
'admin_password' => 'nullable|string|min:8',
|
||||
'admin_name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
DB::transaction(function () use ($validated) {
|
||||
$company = Company::create([
|
||||
'name' => $validated['name'],
|
||||
'code' => $validated['code'],
|
||||
'tax_id' => $validated['tax_id'] ?? null,
|
||||
'contact_name' => $validated['contact_name'] ?? null,
|
||||
'contact_phone' => $validated['contact_phone'] ?? null,
|
||||
'contact_email' => $validated['contact_email'] ?? null,
|
||||
'valid_until' => $validated['valid_until'] ?? null,
|
||||
'status' => $validated['status'],
|
||||
'note' => $validated['note'] ?? null,
|
||||
]);
|
||||
|
||||
// 如果有填寫帳號資訊,則建立管理員帳號
|
||||
if (!empty($validated['admin_username']) && !empty($validated['admin_password'])) {
|
||||
$user = \App\Models\System\User::create([
|
||||
'company_id' => $company->id,
|
||||
'username' => $validated['admin_username'],
|
||||
'password' => \Illuminate\Support\Facades\Hash::make($validated['admin_password']),
|
||||
'name' => $validated['admin_name'] ?: ($validated['contact_name'] ?: $validated['name']),
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
// 綁定客戶管理員角色
|
||||
$user->assignRole('tenant-admin');
|
||||
}
|
||||
});
|
||||
|
||||
return redirect()->back()->with('success', __('Customer created successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Company $company)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'code' => 'required|string|max:50|unique:companies,code,' . $company->id,
|
||||
'tax_id' => 'nullable|string|max:50',
|
||||
'contact_name' => 'required|string|max:255',
|
||||
'contact_phone' => 'nullable|string|max:50',
|
||||
'contact_email' => 'nullable|email|max:255',
|
||||
'valid_until' => 'nullable|date',
|
||||
'status' => 'required|boolean',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$company->update($validated);
|
||||
|
||||
return redirect()->back()->with('success', __('Customer updated successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Company $company)
|
||||
{
|
||||
if ($company->users()->count() > 0) {
|
||||
return redirect()->back()->with('error', __('Cannot delete company with active accounts.'));
|
||||
}
|
||||
|
||||
$company->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('Customer deleted successfully.'));
|
||||
}
|
||||
}
|
||||
@@ -8,26 +8,34 @@ use Illuminate\Http\Request;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
// 每頁顯示筆數限制 (預設為 10)
|
||||
$perPage = $request->get('limit', 10);
|
||||
|
||||
// 從資料庫獲取真實統計數據
|
||||
$totalRevenue = \App\Models\Member\MemberWallet::sum('balance');
|
||||
$activeMachines = Machine::where('status', 'online')->count();
|
||||
$alertsPending = Machine::where('status', 'error')->count();
|
||||
$memberCount = \App\Models\Member\Member::count();
|
||||
|
||||
// 獲取最新動態 (最近 3 筆機台日誌)
|
||||
$latestActivities = \App\Models\Machine\MachineLog::with('machine')
|
||||
// 獲取機台列表 (分頁)
|
||||
$machines = Machine::when($request->search, function($query, $search) {
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('serial_no', 'like', "%{$search}%");
|
||||
});
|
||||
})
|
||||
->latest()
|
||||
->limit(3)
|
||||
->get();
|
||||
->paginate($perPage)
|
||||
->withQueryString();
|
||||
|
||||
return view('admin.dashboard', compact(
|
||||
'totalRevenue',
|
||||
'activeMachines',
|
||||
'alertsPending',
|
||||
'memberCount',
|
||||
'latestActivities'
|
||||
'machines'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,14 +34,6 @@ class DataConfigController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
// 帳號管理
|
||||
public function accounts()
|
||||
{
|
||||
return view('admin.placeholder', [
|
||||
'title' => '帳號管理',
|
||||
'description' => '主帳號管理',
|
||||
]);
|
||||
}
|
||||
|
||||
// 子帳號管理
|
||||
public function subAccounts()
|
||||
|
||||
@@ -13,12 +13,14 @@ class MachineController extends AdminController
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$limit = $request->input('limit', 10);
|
||||
$machines = Machine::query()
|
||||
->when($request->status, function ($query, $status) {
|
||||
return $query->where('status', $status);
|
||||
})
|
||||
->latest()
|
||||
->paginate(10);
|
||||
->paginate($limit)
|
||||
->withQueryString();
|
||||
|
||||
return view('admin.machines.index', compact('machines'));
|
||||
}
|
||||
@@ -40,6 +42,7 @@ class MachineController extends AdminController
|
||||
*/
|
||||
public function logs(Request $request): View
|
||||
{
|
||||
$limit = $request->input('limit', 20);
|
||||
$logs = \App\Models\Machine\MachineLog::with('machine')
|
||||
->when($request->level, function ($query, $level) {
|
||||
return $query->where('level', $level);
|
||||
@@ -48,7 +51,7 @@ class MachineController extends AdminController
|
||||
return $query->where('machine_id', $machineId);
|
||||
})
|
||||
->latest()
|
||||
->paginate(20);
|
||||
->paginate($limit)->withQueryString();
|
||||
|
||||
$machines = Machine::select('id', 'name')->get();
|
||||
|
||||
|
||||
@@ -91,10 +91,67 @@ class PermissionController extends Controller
|
||||
// 權限角色設定
|
||||
public function roles()
|
||||
{
|
||||
return view('admin.placeholder', [
|
||||
'title' => '權限角色設定',
|
||||
'description' => '角色權限組合設定',
|
||||
$limit = request()->input('limit', 10);
|
||||
$roles = \Spatie\Permission\Models\Role::withCount('users')->latest()->paginate($limit)->withQueryString();
|
||||
return view('admin.permission.roles', compact('roles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created role in storage.
|
||||
*/
|
||||
public function storeRole(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255|unique:roles,name',
|
||||
]);
|
||||
|
||||
\Spatie\Permission\Models\Role::create([
|
||||
'name' => $validated['name'],
|
||||
'guard_name' => 'web',
|
||||
'is_system' => false,
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', __('Role created successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified role in storage.
|
||||
*/
|
||||
public function updateRole(Request $request, $id)
|
||||
{
|
||||
$role = \Spatie\Permission\Models\Role::findOrFail($id);
|
||||
|
||||
if ($role->is_system) {
|
||||
return redirect()->back()->with('error', __('System roles cannot be renamed.'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255|unique:roles,name,' . $id,
|
||||
]);
|
||||
|
||||
$role->update(['name' => $validated['name']]);
|
||||
|
||||
return redirect()->back()->with('success', __('Role updated successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified role from storage.
|
||||
*/
|
||||
public function destroyRole($id)
|
||||
{
|
||||
$role = \Spatie\Permission\Models\Role::findOrFail($id);
|
||||
|
||||
if ($role->is_system) {
|
||||
return redirect()->back()->with('error', __('System roles cannot be deleted.'));
|
||||
}
|
||||
|
||||
if ($role->users()->count() > 0) {
|
||||
return redirect()->back()->with('error', __('Cannot delete role with active users.'));
|
||||
}
|
||||
|
||||
$role->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('Role deleted successfully.'));
|
||||
}
|
||||
|
||||
// 其他功能管理
|
||||
@@ -106,6 +163,125 @@ class PermissionController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
// 帳號管理
|
||||
public function accounts(Request $request)
|
||||
{
|
||||
$query = \App\Models\System\User::query()->with(['company', 'roles']);
|
||||
|
||||
// 租戶隔離:如果不是系統管理員,則只看自己公司的成員
|
||||
if (!auth()->user()->isSystemAdmin()) {
|
||||
$query->where('company_id', auth()->user()->company_id);
|
||||
}
|
||||
|
||||
// 搜尋
|
||||
if ($search = $request->input('search')) {
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('username', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 公司篩選 (僅限 super-admin)
|
||||
if (auth()->user()->isSystemAdmin() && $request->filled('company_id')) {
|
||||
$query->where('company_id', $request->company_id);
|
||||
}
|
||||
|
||||
$limit = $request->input('limit', 10);
|
||||
$users = $query->latest()->paginate($limit)->withQueryString();
|
||||
$companies = auth()->user()->isSystemAdmin() ? \App\Models\System\Company::all() : collect();
|
||||
|
||||
return view('admin.data-config.accounts', compact('users', 'companies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created account in storage.
|
||||
*/
|
||||
public function storeAccount(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'username' => 'required|string|max:255|unique:users,username',
|
||||
'email' => 'nullable|email|max:255|unique:users,email',
|
||||
'password' => 'required|string|min:8',
|
||||
'role' => 'required|string',
|
||||
'status' => 'required|boolean',
|
||||
'company_id' => 'nullable|exists:companies,id',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
]);
|
||||
|
||||
$user = \App\Models\System\User::create([
|
||||
'name' => $validated['name'],
|
||||
'username' => $validated['username'],
|
||||
'email' => $validated['email'],
|
||||
'password' => \Illuminate\Support\Facades\Hash::make($validated['password']),
|
||||
'status' => $validated['status'],
|
||||
'company_id' => auth()->user()->isSystemAdmin() ? $validated['company_id'] : auth()->user()->company_id,
|
||||
'phone' => $validated['phone'],
|
||||
]);
|
||||
|
||||
$user->assignRole($validated['role']);
|
||||
|
||||
return redirect()->back()->with('success', __('Account created successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified account in storage.
|
||||
*/
|
||||
public function updateAccount(Request $request, $id)
|
||||
{
|
||||
$user = \App\Models\System\User::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'username' => 'required|string|max:255|unique:users,username,' . $id,
|
||||
'email' => 'nullable|email|max:255|unique:users,email,' . $id,
|
||||
'password' => 'nullable|string|min:8',
|
||||
'role' => 'required|string',
|
||||
'status' => 'required|boolean',
|
||||
'company_id' => 'nullable|exists:companies,id',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
]);
|
||||
|
||||
$updateData = [
|
||||
'name' => $validated['name'],
|
||||
'username' => $validated['username'],
|
||||
'email' => $validated['email'],
|
||||
'status' => $validated['status'],
|
||||
'phone' => $validated['phone'],
|
||||
];
|
||||
|
||||
if (auth()->user()->isSystemAdmin()) {
|
||||
$updateData['company_id'] = $validated['company_id'];
|
||||
}
|
||||
|
||||
if (!empty($validated['password'])) {
|
||||
$updateData['password'] = \Illuminate\Support\Facades\Hash::make($validated['password']);
|
||||
}
|
||||
|
||||
$user->update($updateData);
|
||||
|
||||
$user->syncRoles([$validated['role']]);
|
||||
|
||||
return redirect()->back()->with('success', __('Account updated successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified account from storage.
|
||||
*/
|
||||
public function destroyAccount($id)
|
||||
{
|
||||
$user = \App\Models\System\User::findOrFail($id);
|
||||
|
||||
if ($user->id === auth()->id()) {
|
||||
return redirect()->back()->with('error', __('You cannot delete your own account.'));
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('Account deleted successfully.'));
|
||||
}
|
||||
|
||||
// AI智能預測
|
||||
public function aiPrediction()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user