fix:coach相關改provider

This commit is contained in:
2025-05-12 01:22:09 +08:00
parent 3d7660a24c
commit 125d4bf8d8
10 changed files with 433 additions and 165 deletions
+121 -90
View File
@@ -5,18 +5,19 @@ namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User; use App\Models\User;
use App\Models\AdminProfile; use App\Models\AdminProfile;
use App\Models\CoachProfile; use App\Models\ProviderProfile;
use App\Models\MemberProfile; use App\Models\MemberProfile;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Password;
class AuthController extends Controller class AuthController extends Controller
{ {
// 科定規範角色 // 科定規範角色
private const ROLE_MEMBER = 'member'; private const ROLE_MEMBER = 'member';
private const ROLE_COACH = 'coach'; private const ROLE_PROVIDER = 'provider';
private const ROLE_ADMIN = 'admin'; private const ROLE_ADMIN = 'admin';
/** /**
@@ -65,13 +66,13 @@ class AuthController extends Controller
if ($role === self::ROLE_MEMBER) { if ($role === self::ROLE_MEMBER) {
$targetUser->load('memberProfile'); $targetUser->load('memberProfile');
} else { } else {
$targetUser->load('coachProfile'); $targetUser->load('providerProfile');
} }
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'data' => $userData, 'data' => $userData,
'profile' => $role === self::ROLE_MEMBER ? $targetUser->memberProfile : $targetUser->coachProfile, 'profile' => $role === self::ROLE_MEMBER ? $targetUser->memberProfile : $targetUser->providerProfile,
]); ]);
} }
@@ -144,7 +145,7 @@ class AuthController extends Controller
// 撤銷目前的 token // 撤銷目前的 token
$request->user()->currentAccessToken()->delete(); $request->user()->currentAccessToken()->delete();
$roleText = $role === self::ROLE_MEMBER ? '會員' : ($role === self::ROLE_COACH ? '教練' : '管理員'); $roleText = $role === self::ROLE_MEMBER ? '會員' : ($role === self::ROLE_PROVIDER ? '服務提供者' : '管理員');
return response()->json([ return response()->json([
'status' => true, 'status' => true,
@@ -197,8 +198,8 @@ class AuthController extends Controller
// 根據角色加載對應的資料 // 根據角色加載對應的資料
if ($user->isAdmin()) { if ($user->isAdmin()) {
$user->load('adminProfile'); $user->load('adminProfile');
} elseif ($user->isCoach()) { } elseif ($user->isProvider()) {
$user->load('coachProfile'); $user->load('providerProfile');
} elseif ($user->isMember()) { } elseif ($user->isMember()) {
$user->load('memberProfile'); $user->load('memberProfile');
} }
@@ -209,9 +210,9 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 會員註冊 * 會員註冊
*/ */
public function registerMember(Request $request) public function registerMember(Request $request)
{ {
// 驗證請求數據 // 驗證請求數據
@@ -262,9 +263,9 @@ class AuthController extends Controller
], 201); ], 201);
} }
/** /**
* 會員登入 * 會員登入
*/ */
public function loginMember(Request $request) public function loginMember(Request $request)
{ {
// 驗證請求數據 // 驗證請求數據
@@ -319,9 +320,9 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 會員登出 * 會員登出
*/ */
public function logoutMember(Request $request) public function logoutMember(Request $request)
{ {
// 確保只有會員可以使用這個方法 // 確保只有會員可以使用這個方法
@@ -342,9 +343,9 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 取得會員個人資料 * 取得會員個人資料
*/ */
public function memberProfile(Request $request) public function memberProfile(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
@@ -365,9 +366,9 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 更新會員個人資料 * 更新會員個人資料
*/ */
public function updateMemberProfile(Request $request) public function updateMemberProfile(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
@@ -416,9 +417,9 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 修改會員密碼 * 修改會員密碼
*/ */
public function changeMemberPassword(Request $request) public function changeMemberPassword(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
@@ -462,10 +463,10 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 教練註冊 * 服務提供者註冊
*/ */
public function registerCoach(Request $request) public function registerProvider(Request $request)
{ {
// 驗證請求數據 // 驗證請求數據
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
@@ -473,8 +474,13 @@ class AuthController extends Controller
'email' => 'required|string|email|max:255|unique:users', 'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed', 'password' => 'required|string|min:6|confirmed',
'phone' => 'nullable|string|max:20', 'phone' => 'nullable|string|max:20',
'bio' => 'nullable|string', 'business_name' => 'required|string|max:255',
'expertise' => 'nullable|string|max:100', 'description' => 'nullable|string',
'contact_person' => 'nullable|string|max:100',
'contact_phone' => 'nullable|string|max:20',
'contact_email' => 'nullable|string|email|max:255',
'address' => 'nullable|string|max:255',
'business_hours' => 'nullable|string|max:100',
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
@@ -491,22 +497,27 @@ class AuthController extends Controller
'email' => $request->email, 'email' => $request->email,
'password' => Hash::make($request->password), 'password' => Hash::make($request->password),
'phone' => $request->phone, 'phone' => $request->phone,
'role' => 'coach', // 強制為教練角色 'role' => 'provider', // 強制為服務提供者角色
]); ]);
// 創建教練資料 // 創建服務提供者資料
CoachProfile::create([ ProviderProfile::create([
'user_id' => $user->id, 'user_id' => $user->id,
'bio' => $request->bio, 'business_name' => $request->business_name,
'expertise' => $request->expertise, 'description' => $request->description ?? null,
'contact_person' => $request->contact_person ?? null,
'contact_phone' => $request->contact_phone ?? null,
'contact_email' => $request->contact_email ?? null,
'address' => $request->address ?? null,
'business_hours' => $request->business_hours ?? null,
]); ]);
// 創建 API token // 創建 API token
$token = $user->createToken('auth_token')->plainTextToken; $token = $user->createToken('auth_token')->plainTextToken;
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'message' => '教練註冊成功', 'message' => '服務提供者註冊成功',
'data' => [ 'data' => [
'user' => $user, 'user' => $user,
'token' => $token, 'token' => $token,
@@ -515,10 +526,10 @@ class AuthController extends Controller
], 201); ], 201);
} }
/** /**
* 教練登入 * 服務提供者登入
*/ */
public function loginCoach(Request $request) public function loginProvider(Request $request)
{ {
// 驗證請求數據 // 驗證請求數據
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
@@ -536,7 +547,7 @@ class AuthController extends Controller
// 檢查用戶是否存在 // 檢查用戶是否存在
$user = User::where('email', $request->email) $user = User::where('email', $request->email)
->where('role', 'coach') // 只驗證教練 ->where('role', 'provider') // 只驗證服務提供者
->first(); ->first();
// 檢查密碼 // 檢查密碼
@@ -558,8 +569,8 @@ class AuthController extends Controller
// 創建 API token // 創建 API token
$token = $user->createToken('auth_token')->plainTextToken; $token = $user->createToken('auth_token')->plainTextToken;
// 加載教練資料 // 加載服務提供者資料
$user->load('coachProfile'); $user->load('providerProfile');
return response()->json([ return response()->json([
'status' => true, 'status' => true,
@@ -572,14 +583,14 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 教練登出 * 服務提供者登出
*/ */
public function logoutCoach(Request $request) public function logoutProvider(Request $request)
{ {
// 確保只有教練可以使用這個方法 // 確保只有服務提供者可以使用這個方法
$user = $request->user(); $user = $request->user();
if ($user->role !== 'coach') { if ($user->role !== 'provider') {
return response()->json([ return response()->json([
'status' => false, 'status' => false,
'message' => '無權限存取' 'message' => '無權限存取'
@@ -591,26 +602,26 @@ class AuthController extends Controller
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'message' => '教練登出成功' 'message' => '服務提供者登出成功'
]); ]);
} }
/** /**
* 取得教練個人資料 * 取得服務提供者資料
*/ */
public function coachProfile(Request $request) public function providerProfile(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
// 確保只有教練可以使用這個方法 // 確保只有服務提供者可以使用這個方法
if ($user->role !== 'coach') { if ($user->role !== 'provider') {
return response()->json([ return response()->json([
'status' => false, 'status' => false,
'message' => '無權限存取' 'message' => '無權限存取'
], 403); ], 403);
} }
// 加載教練資料 // 加載服務提供者資料
$user->load('coachProfile'); $user->load('providerProfile');
return response()->json([ return response()->json([
'status' => true, 'status' => true,
@@ -618,14 +629,14 @@ class AuthController extends Controller
]); ]);
} }
/** /**
* 更新教練個人資料 * 更新服務提供者資料
*/ */
public function updateCoachProfile(Request $request) public function updateProviderProfile(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
// 確保只有教練可以使用這個方法 // 確保只有服務提供者可以使用這個方法
if ($user->role !== 'coach') { if ($user->role !== 'provider') {
return response()->json([ return response()->json([
'status' => false, 'status' => false,
'message' => '無權限存取' 'message' => '無權限存取'
@@ -637,8 +648,13 @@ class AuthController extends Controller
'name' => 'nullable|string|max:255', 'name' => 'nullable|string|max:255',
'email' => 'nullable|string|email|max:255|unique:users,email,' . $user->id, 'email' => 'nullable|string|email|max:255|unique:users,email,' . $user->id,
'phone' => 'nullable|string|max:20', 'phone' => 'nullable|string|max:20',
'bio' => 'nullable|string', 'business_name' => 'nullable|string|max:255',
'expertise' => 'nullable|string|max:100', 'description' => 'nullable|string',
'contact_person' => 'nullable|string|max:100',
'contact_phone' => 'nullable|string|max:20',
'contact_email' => 'nullable|string|email|max:255',
'address' => 'nullable|string|max:255',
'business_hours' => 'nullable|string|max:100',
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
@@ -661,36 +677,51 @@ class AuthController extends Controller
} }
$user->save(); $user->save();
// 更新教練資料 // 更新服務提供者資料
if ($request->has('bio') || $request->has('expertise')) { $providerProfile = $user->providerProfile;
$coachProfile = $user->coachProfile;
if ($request->has('bio')) { if ($request->has('business_name')) {
$coachProfile->bio = $request->bio; $providerProfile->business_name = $request->business_name;
}
if ($request->has('expertise')) {
$coachProfile->expertise = $request->expertise;
}
$coachProfile->save();
} }
if ($request->has('description')) {
$providerProfile->description = $request->description;
}
if ($request->has('contact_person')) {
$providerProfile->contact_person = $request->contact_person;
}
if ($request->has('contact_phone')) {
$providerProfile->contact_phone = $request->contact_phone;
}
if ($request->has('contact_email')) {
$providerProfile->contact_email = $request->contact_email;
}
if ($request->has('address')) {
$providerProfile->address = $request->address;
}
if ($request->has('business_hours')) {
$providerProfile->business_hours = $request->business_hours;
}
$providerProfile->save();
// 加載教練資料 // 加載服務提供者資料
$user->load('coachProfile'); $user->load('providerProfile');
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'message' => '教練資料已更新', 'message' => '服務提供者資料已更新',
'data' => $user, 'data' => $user,
]); ]);
} }
/** /**
* 修改教練密碼 * 修改服務提供者密碼
*/ */
public function changeCoachPassword(Request $request) public function changeProviderPassword(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
// 確保只有教練可以使用這個方法 // 確保只有服務提供者可以使用這個方法
if ($user->role !== 'coach') { if ($user->role !== 'provider') {
return response()->json([ return response()->json([
'status' => false, 'status' => false,
'message' => '無權限存取' 'message' => '無權限存取'
@@ -1006,11 +1037,11 @@ class AuthController extends Controller
} }
/** /**
* 查詢教練資料 * 查詢服務提供者資料
* 只有管理員可以使用這個方法 * 只有管理員可以使用這個方法
*/ */
public function checkCoach(int $id) public function checkProvider(int $id)
{ {
return $this->checkUser(self::ROLE_COACH, $id); return $this->checkUser(self::ROLE_PROVIDER, $id);
} }
} }
+16
View File
@@ -5,6 +5,22 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* @OA\Schema(
* schema="AdminProfile",
* title="管理員個人資料",
* description="管理員的詳細個人資料",
* @OA\Property(property="id", type="integer", format="int64", example=1, description="資料ID"),
* @OA\Property(property="user_id", type="integer", format="int64", example=1, description="關聯的使用者ID"),
* @OA\Property(property="position", type="string", example="系統管理員", description="職位"),
* @OA\Property(property="department", type="string", example="IT部門", description="部門"),
* @OA\Property(property="permissions", type="array", description="權限列表",
* @OA\Items(type="string", example="manage_users")
* ),
* @OA\Property(property="created_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="創建時間"),
* @OA\Property(property="updated_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="更新時間")
* )
*/
class AdminProfile extends Model class AdminProfile extends Model
{ {
use HasFactory; use HasFactory;
+17
View File
@@ -4,6 +4,23 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* @OA\Schema(
* schema="CoachProfile",
* title="教練個人資料",
* description="教練的詳細個人資料",
* @OA\Property(property="id", type="integer", format="int64", example=1, description="資料ID"),
* @OA\Property(property="user_id", type="integer", format="int64", example=1, description="關聯的使用者ID"),
* @OA\Property(property="bio", type="string", example="專業潛水教練,擁有10年教學經驗", description="個人簡介"),
* @OA\Property(property="expertise", type="string", example="自由潛水,水肺潛水", description="專長領域"),
* @OA\Property(property="certification", type="string", example="PADI專業潛水教練", description="證照資訊"),
* @OA\Property(property="experience", type="string", example="10年教學經驗,帶領超過500名學員", description="教學經驗"),
* @OA\Property(property="rating", type="number", format="float", example=4.8, description="評分"),
* @OA\Property(property="availability", type="string", example="週一至週五 09:00-18:00", description="可授課時間"),
* @OA\Property(property="created_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="創建時間"),
* @OA\Property(property="updated_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="更新時間")
* )
*/
class CoachProfile extends Model class CoachProfile extends Model
{ {
/** /**
+21 -5
View File
@@ -5,6 +5,22 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* @OA\Schema(
* schema="MemberProfile",
* title="會員個人資料",
* description="會員的詳細個人資料",
* @OA\Property(property="id", type="integer", format="int64", example=1, description="資料ID"),
* @OA\Property(property="user_id", type="integer", format="int64", example=1, description="關聯的使用者ID"),
* @OA\Property(property="birthday", type="string", format="date", example="1990-01-01", description="生日"),
* @OA\Property(property="gender", type="string", enum={"male", "female", "other"}, example="male", description="性別"),
* @OA\Property(property="address", type="string", example="台北市信義區某街123號", description="地址"),
* @OA\Property(property="emergency_contact", type="string", example="王大明", description="緊急聯絡人"),
* @OA\Property(property="emergency_phone", type="string", example="0987654321", description="緊急聯絡電話"),
* @OA\Property(property="created_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="創建時間"),
* @OA\Property(property="updated_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="更新時間")
* )
*/
class MemberProfile extends Model class MemberProfile extends Model
{ {
use HasFactory; use HasFactory;
@@ -67,17 +83,17 @@ class MemberProfile extends Model
} }
/** /**
* 獲取會員的教練 * 獲取會員的服務提供者
*/ */
public function coaches() public function providers()
{ {
return $this->hasManyThrough( return $this->hasManyThrough(
CoachProfile::class, ProviderProfile::class,
'coach_member', 'provider_member',
'member_id', 'member_id',
'user_id', 'user_id',
'user_id', 'user_id',
'coach_id' 'provider_id'
); );
} }
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @OA\Schema(
* schema="ProviderProfile",
* title="潛水業者資料",
* description="潛水業者的詳細資料",
* @OA\Property(property="id", type="integer", format="int64", example=1, description="資料ID"),
* @OA\Property(property="user_id", type="integer", format="int64", example=1, description="關聯的使用者ID"),
* @OA\Property(property="business_name", type="string", example="藍海潛水中心", description="業者名稱"),
* @OA\Property(property="business_license", type="string", example="A123456789", description="營業執照號碼"),
* @OA\Property(property="description", type="string", example="專業潛水中心,提供各種潛水課程和裝備租賃服務", description="業者描述"),
* @OA\Property(property="contact_person", type="string", example="張三", description="聯絡人"),
* @OA\Property(property="contact_phone", type="string", example="0912345678", description="聯絡電話"),
* @OA\Property(property="contact_email", type="string", example="contact@bluedive.com", description="聯絡電子郵件"),
* @OA\Property(property="address", type="string", example="台灣屏東縣恆春鎮XXX路123號", description="營業地址"),
* @OA\Property(property="dive_sites", type="string", example="墾丁,綠島,蘭嶼", description="提供的潛點"),
* @OA\Property(property="services", type="string", example="體驗潛水,初級潛水課程,進階潛水課程,裝備租賃", description="提供的服務"),
* @OA\Property(property="certifications", type="string", example="PADI五星級潛水中心,SSI認證中心", description="業者相關認證"),
* @OA\Property(property="facilities", type="string", example="空氣填充站,沖洗區,更衣室,休息區", description="設施"),
* @OA\Property(property="business_hours", type="string", example="週一至週五 09:00-18:00,週六日 08:00-19:00", description="營業時間"),
* @OA\Property(property="is_verified", type="boolean", example=true, description="是否通過平台驗證"),
* @OA\Property(property="rating", type="number", format="float", example=4.8, description="評分"),
* @OA\Property(property="website", type="string", example="https://www.bluedive.com", description="官方網站"),
* @OA\Property(property="social_media", type="string", example="https://www.facebook.com/bluedive", description="社群媒體連結"),
* @OA\Property(property="logo_url", type="string", example="https://example.com/logo.png", description="業者標誌URL"),
* @OA\Property(property="banner_url", type="string", example="https://example.com/banner.png", description="業者橫幅URL"),
* @OA\Property(property="is_active", type="boolean", example=true, description="是否啟用"),
* @OA\Property(property="created_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="創建時間"),
* @OA\Property(property="updated_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="更新時間")
* )
*/
class ProviderProfile extends Model
{
/**
* 可以批量分配的屬性
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'business_name',
'business_license',
'description',
'contact_person',
'contact_phone',
'contact_email',
'address',
'dive_sites',
'services',
'certifications',
'facilities',
'business_hours',
'is_verified',
'rating',
'website',
'social_media',
'logo_url',
'banner_url',
'is_active'
];
/**
* 與用戶的關聯
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
+37 -19
View File
@@ -11,6 +11,24 @@ use Laravel\Sanctum\HasApiTokens;
* User 使用者模型 * User 使用者模型
* *
* 對應 users 資料表,並提供角色判斷、關聯資料取得等功能。 * 對應 users 資料表,並提供角色判斷、關聯資料取得等功能。
*
* @OA\Schema(
* schema="User",
* title="使用者",
* description="使用者資料模型",
* @OA\Property(property="id", type="integer", format="int64", example=1, description="使用者ID"),
* @OA\Property(property="name", type="string", example="王小明", description="使用者姓名"),
* @OA\Property(property="email", type="string", format="email", example="user@example.com", description="電子郵件"),
* @OA\Property(property="phone", type="string", example="0912345678", description="電話號碼"),
* @OA\Property(property="role", type="string", enum={"member", "provider", "admin"}, example="member", description="角色"),
* @OA\Property(property="is_active", type="boolean", example=true, description="是否啟用"),
* @OA\Property(property="email_verified_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="電子郵件驗證時間"),
* @OA\Property(property="created_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="創建時間"),
* @OA\Property(property="updated_at", type="string", format="date-time", example="2023-01-01T00:00:00.000000Z", description="更新時間"),
* @OA\Property(property="memberProfile", type="object", ref="#/components/schemas/MemberProfile", description="會員詳細資料"),
* @OA\Property(property="providerProfile", type="object", ref="#/components/schemas/ProviderProfile", description="服務提供者詳細資料"),
* @OA\Property(property="adminProfile", type="object", ref="#/components/schemas/AdminProfile", description="管理員詳細資料")
* )
*/ */
class User extends Authenticatable class User extends Authenticatable
{ {
@@ -75,15 +93,15 @@ class User extends Authenticatable
} }
/** /**
* 判斷用戶是否為教練 * 判斷用戶是否為服務提供者
*/ */
/** /**
* 判斷用戶是否為教練 * 判斷用戶是否為服務提供者
* @return bool * @return bool
*/ */
public function isCoach() public function isProvider()
{ {
return $this->role === 'coach'; return $this->role === 'provider';
} }
/** /**
@@ -111,15 +129,15 @@ class User extends Authenticatable
} }
/** /**
* 獲取用戶的教練資料 * 獲取用戶的服務提供者資料
*/ */
/** /**
* 取得用戶的教練詳細資料(關聯 coach_profiles * 取得用戶的服務提供者詳細資料(關聯 provider_profiles
* @return \Illuminate\Database\Eloquent\Relations\HasOne * @return \Illuminate\Database\Eloquent\Relations\HasOne
*/ */
public function coachProfile() public function providerProfile()
{ {
return $this->hasOne(CoachProfile::class); return $this->hasOne(ProviderProfile::class);
} }
/** /**
@@ -145,45 +163,45 @@ class User extends Authenticatable
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
return $this->adminProfile; return $this->adminProfile;
} elseif ($this->isCoach()) { } elseif ($this->isProvider()) {
return $this->coachProfile; return $this->providerProfile;
} else { } else {
return $this->memberProfile; return $this->memberProfile;
} }
} }
/** /**
* 獲取教練的會員 (僅適用於教練角色) * 獲取服務提供者的會員 (僅適用於服務提供者角色)
*/ */
/** /**
* 取得教練所帶的會員(僅教練角色適用) * 取得服務提供者所帶的會員(僅服務提供者角色適用)
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|null * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|null
*/ */
public function members() public function members()
{ {
if (!$this->isCoach()) { if (!$this->isProvider()) {
return null; return null;
} }
return $this->belongsToMany(User::class, 'coach_member', 'coach_id', 'member_id') return $this->belongsToMany(User::class, 'provider_member', 'provider_id', 'member_id')
->where('role', 'member'); ->where('role', 'member');
} }
/** /**
* 獲取會員的教練 (僅適用於會員角色) * 獲取會員的服務提供者 (僅適用於會員角色)
*/ */
/** /**
* 取得會員對應的教練(僅會員角色適用) * 取得會員對應的服務提供者(僅會員角色適用)
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|null * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|null
*/ */
public function coaches() public function providers()
{ {
if (!$this->isMember()) { if (!$this->isMember()) {
return null; return null;
} }
return $this->belongsToMany(User::class, 'coach_member', 'member_id', 'coach_id') return $this->belongsToMany(User::class, 'provider_member', 'member_id', 'provider_id')
->where('role', 'coach'); ->where('role', 'provider');
} }
/** /**
@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('provider_profiles', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('business_name')->nullable();
$table->string('business_license')->nullable()->comment('營業執照號碼');
$table->text('description')->nullable()->comment('業者描述');
$table->string('contact_person')->nullable()->comment('聯絡人');
$table->string('contact_phone')->nullable();
$table->string('contact_email')->nullable();
$table->string('address')->nullable()->comment('營業地址');
$table->text('dive_sites')->nullable()->comment('提供的潛點');
$table->text('services')->nullable()->comment('提供的服務,例如:訓練課程、裝備出租、潛導服務等');
$table->text('certifications')->nullable()->comment('業者相關認證');
$table->text('facilities')->nullable()->comment('設施,如壓縮空氣設備、沖洗區等');
$table->string('business_hours')->nullable()->comment('營業時間');
$table->boolean('is_verified')->default(false)->comment('是否通過平台驗證');
$table->float('rating')->default(0)->comment('評分');
$table->string('website')->nullable()->comment('官方網站');
$table->string('social_media')->nullable()->comment('社群媒體連結');
$table->string('logo_url')->nullable()->comment('業者標誌URL');
$table->string('banner_url')->nullable()->comment('業者橫幅URL');
$table->boolean('is_active')->default(true)->comment('是否啟用');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('provider_profiles');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 創建 provider_member 表
Schema::create('provider_member', function (Blueprint $table) {
$table->id(); // 關聯主鍵ID
$table->foreignId('provider_id')->constrained('users'); // 關聯服務提供者(users表)
$table->foreignId('member_id')->constrained('users'); // 關聯會員(users表)
$table->timestamps(); // 建立與更新時間
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// 刪除 provider_member 表
Schema::dropIfExists('provider_member');
}
};
+54 -36
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CFDive平台 - 管理員與教練註冊測試</title> <title>CFDive平台 - 管理員與服務提供者註冊測試</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style> <style>
.container { max-width: 800px; margin-top: 50px; } .container { max-width: 800px; margin-top: 50px; }
@@ -15,14 +15,14 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1 class="mb-4">CFDive平台 - 管理員與教練註冊測試</h1> <h1 class="mb-4">CFDive平台 - 管理員與服務提供者註冊測試</h1>
<ul class="nav nav-tabs" id="userTypeTabs" role="tablist"> <ul class="nav nav-tabs" id="userTypeTabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active" id="admin-tab" data-bs-toggle="tab" data-bs-target="#admin" type="button" role="tab">管理員註冊</button> <button class="nav-link active" id="admin-tab" data-bs-toggle="tab" data-bs-target="#admin" type="button" role="tab">管理員註冊</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="coach-tab" data-bs-toggle="tab" data-bs-target="#coach" type="button" role="tab">教練註冊</button> <button class="nav-link" id="provider-tab" data-bs-toggle="tab" data-bs-target="#provider" type="button" role="tab">服務提供者註冊</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="login-tab" data-bs-toggle="tab" data-bs-target="#login" type="button" role="tab">登入</button> <button class="nav-link" id="login-tab" data-bs-toggle="tab" data-bs-target="#login" type="button" role="tab">登入</button>
@@ -82,48 +82,63 @@
</div> </div>
<!-- 教練註冊表單 --> <!-- 教練註冊表單 -->
<div class="tab-pane fade" id="coach" role="tabpanel"> <div class="tab-pane fade" id="provider" role="tabpanel">
<h3>教練註冊</h3> <h3>服務提供者註冊</h3>
<form id="coachForm"> <form id="providerForm">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="coachName" class="form-label">姓名</label> <label for="providerName" class="form-label">姓名</label>
<input type="text" class="form-control" id="coachName" value="測試教練" required> <input type="text" class="form-control" id="providerName" value="測試服務提供者" required>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="coachEmail" class="form-label">電子郵件</label> <label for="providerEmail" class="form-label">電子郵件</label>
<input type="email" class="form-control" id="coachEmail" value="coach@example.com" required> <input type="email" class="form-control" id="providerEmail" value="provider@example.com" required>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="coachPassword" class="form-label">密碼</label> <label for="providerPassword" class="form-label">密碼</label>
<input type="password" class="form-control" id="coachPassword" value="Coach123!" required> <input type="password" class="form-control" id="providerPassword" value="Provider123!" required>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="coachPasswordConfirmation" class="form-label">確認密碼</label> <label for="providerPasswordConfirmation" class="form-label">確認密碼</label>
<input type="password" class="form-control" id="coachPasswordConfirmation" value="Coach123!" required> <input type="password" class="form-control" id="providerPasswordConfirmation" value="Provider123!" required>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<label for="coachPhone" class="form-label">電話</label> <label for="providerPhone" class="form-label">電話</label>
<input type="text" class="form-control" id="coachPhone" value="0987654321"> <input type="text" class="form-control" id="providerPhone" value="0987654321">
</div> </div>
<div class="col-md-8"> <div class="col-md-4">
<label for="coachExpertise" class="form-label">專長</label> <label for="businessName" class="form-label">業者名稱</label>
<input type="text" class="form-control" id="coachExpertise" value="自由潛水,水肺潛水" required> <input type="text" class="form-control" id="businessName" value="藍海潛水中心" required>
</div>
<div class="col-md-4">
<label for="contactPerson" class="form-label">聯絡人</label>
<input type="text" class="form-control" id="contactPerson" value="王大師" required>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="contactEmail" class="form-label">聯絡電子郵件</label>
<input type="email" class="form-control" id="contactEmail" value="contact@bluedive.com">
</div>
<div class="col-md-6">
<label for="address" class="form-label">營業地址</label>
<input type="text" class="form-control" id="address" value="台灣屏東縣恆春鎮大路123號">
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="coachBio" class="form-label">個人簡介</label> <label for="description" class="form-label">業者描述</label>
<textarea class="form-control" id="coachBio" rows="3" required>擁有10年潛水教學經驗,專精於開放水域和洞穴潛水</textarea> <textarea class="form-control" id="description" rows="3" required>專業潛水中心,提供各種潛水服務、潛水行程和潛水課程</textarea>
</div> </div>
<button type="submit" class="btn btn-success">註冊教練</button> <button type="submit" class="btn btn-success">註冊服務提供者</button>
</form> </form>
</div> </div>
@@ -134,7 +149,7 @@
<div class="col-md-4"> <div class="col-md-4">
<select class="form-select mb-3" id="loginType"> <select class="form-select mb-3" id="loginType">
<option value="admin">管理員</option> <option value="admin">管理員</option>
<option value="coach">教練</option> <option value="provider">服務提供者</option>
<option value="member">會員</option> <option value="member">會員</option>
</select> </select>
</div> </div>
@@ -159,7 +174,7 @@
<label for="logoutType" class="form-label">用戶類型</label> <label for="logoutType" class="form-label">用戶類型</label>
<select class="form-select" id="logoutType"> <select class="form-select" id="logoutType">
<option value="member">會員</option> <option value="member">會員</option>
<option value="coach">教練</option> <option value="provider">服務提供者</option>
<option value="admin">管理員</option> <option value="admin">管理員</option>
</select> </select>
</div> </div>
@@ -195,7 +210,7 @@
<div class="tab-pane fade" id="check-user" role="tabpanel"> <div class="tab-pane fade" id="check-user" role="tabpanel">
<h3>查詢會員/教練資料</h3> <h3>查詢會員/教練資料</h3>
<div class="alert alert-info"> <div class="alert alert-info">
<i class="bi bi-info-circle"></i> 此功能為管理員專用,可查詢會員或教練的詳細資料。請先以管理員身份登入才能使用此功能。 <i class="bi bi-info-circle"></i> 此功能為管理員專用,可查詢會員或服務提供者的詳細資料。請先以管理員身份登入才能使用此功能。
</div> </div>
<form id="checkUserForm"> <form id="checkUserForm">
<div class="row mb-3"> <div class="row mb-3">
@@ -203,7 +218,7 @@
<label for="checkUserType" class="form-label">用戶類型</label> <label for="checkUserType" class="form-label">用戶類型</label>
<select class="form-select" id="checkUserType"> <select class="form-select" id="checkUserType">
<option value="member">會員</option> <option value="member">會員</option>
<option value="coach">教練</option> <option value="provider">服務提供者</option>
</select> </select>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@@ -273,21 +288,24 @@
callApi('/api/admin/register', 'POST', data); callApi('/api/admin/register', 'POST', data);
}); });
// 教練註冊 // 服務提供者註冊
document.getElementById('coachForm').addEventListener('submit', function(e) { document.getElementById('providerForm').addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
const data = { const data = {
name: document.getElementById('coachName').value, name: document.getElementById('providerName').value,
email: document.getElementById('coachEmail').value, email: document.getElementById('providerEmail').value,
password: document.getElementById('coachPassword').value, password: document.getElementById('providerPassword').value,
password_confirmation: document.getElementById('coachPasswordConfirmation').value, password_confirmation: document.getElementById('providerPasswordConfirmation').value,
phone: document.getElementById('coachPhone').value, phone: document.getElementById('providerPhone').value,
bio: document.getElementById('coachBio').value, business_name: document.getElementById('businessName').value,
expertise: document.getElementById('coachExpertise').value description: document.getElementById('description').value,
contact_person: document.getElementById('contactPerson').value,
contact_email: document.getElementById('contactEmail').value,
address: document.getElementById('address').value
}; };
callApi('/api/coach/register', 'POST', data); callApi('/api/provider/register', 'POST', data);
}); });
// 登入 // 登入
+15 -15
View File
@@ -39,21 +39,21 @@ Route::middleware(['auth:sanctum'])->prefix('member')->group(function () {
// Route::get('/favorites', [FavoriteController::class, 'memberFavorites']); // Route::get('/favorites', [FavoriteController::class, 'memberFavorites']);
}); });
// 教練註冊/登入 // 服務提供者註冊/登入
Route::post('/coach/register', [AuthController::class, 'registerCoach']); Route::post('/provider/register', [AuthController::class, 'registerProvider']);
Route::post('/coach/login', [AuthController::class, 'loginCoach']); Route::post('/provider/login', [AuthController::class, 'loginProvider']);
// 教練專屬 API(需登入) // 服務提供者專屬 API(需登入)
Route::middleware(['auth:sanctum'])->prefix('coach')->group(function () { Route::middleware(['auth:sanctum'])->prefix('provider')->group(function () {
// 教練登出 // 服務提供者登出
Route::post('/logout', [AuthController::class, 'logoutCoach']); Route::post('/logout', [AuthController::class, 'logoutProvider']);
// 取得教練個人資料 // 取得服務提供者資料
Route::get('/profile', [AuthController::class, 'coachProfile']); Route::get('/profile', [AuthController::class, 'providerProfile']);
// 更新教練個人資料 // 更新服務提供者資料
Route::put('/profile', [AuthController::class, 'updateCoachProfile']); Route::put('/profile', [AuthController::class, 'updateProviderProfile']);
// 修改密碼 // 修改密碼
Route::put('/change-password', [AuthController::class, 'changeCoachPassword']); Route::put('/change-password', [AuthController::class, 'changeProviderPassword']);
// 其他教練專屬 API // 其他服務提供者專屬 API
}); });
// 管理員註冊/登入 // 管理員註冊/登入
@@ -72,8 +72,8 @@ Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
Route::put('/change-password', [AuthController::class, 'changeAdminPassword']); Route::put('/change-password', [AuthController::class, 'changeAdminPassword']);
// 查詢會員資料 // 查詢會員資料
Route::get('/check-member/{id}', [AuthController::class, 'checkMember']); Route::get('/check-member/{id}', [AuthController::class, 'checkMember']);
// 查詢教練資料 // 查詢服務提供者資料
Route::get('/check-coach/{id}', [AuthController::class, 'checkCoach']); Route::get('/check-provider/{id}', [AuthController::class, 'checkProvider']);
// 其他管理員專屬 API // 其他管理員專屬 API
}); });