Hệ Thống Đặt Vé Xem Phim
LVTN_BE/
├── app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── Api/ # API Controllers cho Mobile
│ │ │ ├── Admin/ # Admin Controllers
│ │ │ ├── Partner/ # Partner Controllers
│ │ │ └── Web/ # Public Web Controllers
│ │ ├── Middleware/
│ │ ├── Requests/ # Form Requests
│ │ ├── Resources/ # API Resources
│ │ └── Traits/
│ ├── Models/
│ ├── Services/ # Service Layer
│ ├── Helpers/ # Helper Classes
│ └── Providers/
├── bootstrap/
├── config/
├── database/
│ ├── migrations/
│ └── seeders/
├── doc/
│ ├── api/ # API Documentation (Markdown)
│ ├── html/ # API Documentation (HTML)
│ └── task/ # Task Documentation
├── resources/
│ ├── lang/
│ └── views/
├── routes/
│ ├── api.php
│ └── web.php
└── ...
app/Http/Controllers/Api/: Controllers cho Mobile App APIapp/Http/Controllers/Admin/: Controllers cho Admin Panelapp/Http/Controllers/Partner/: Controllers cho Partner Dashboardapp/Http/Controllers/Web/: Controllers cho Public Websiteapp/Http/Resources/: API Resources để format dữ liệu trả vềapp/Http/Middleware/: Custom Middlewareapp/Http/Requests/: Form Request Validationapp/Services/: Business Logic Layerapp/Models/: Eloquent Modelsapp/Helpers/: Helper Classesdoc/api/: API Documentation (Markdown) - File gốcdoc/html/: API Documentation (HTML) - File HTML được convert từ Markdown để hiển thị trên webdoc/task/: Task Documentationresources/lang/: i18n filesDatabase schema được định nghĩa trong file b.sql. Các bảng chính:
roles - Vai trò (admin, partner, customer)permissions - Quyền hạnrole_permissions - Phân quyền cho từng roleusers - Người dùng (có role_id, avatar_id)media_folders - Thư mục mediamedia_files - File media (ảnh, video)movies - Phimcinemas - Rạp chiếu phimrooms - Phòng chiếuseats - Ghế ngồishowtimes - Suất chiếubookings - Đặt vébooking_seats - Ghế đã đặtreviews - Đánh giá phimfavorite_movies - Phim yêu thíchvouchers - Mã giảm giá$table->foreign('user_id')->references('id')->on('users');
$table->index('user_id'); // BẮT BUỘC
status, email, code, slug, name, date, created_at$table->softDeletes();
$table->index('deleted_at');
$table->enum('status', ['active', 'inactive'])->default('active');
$table->unique(['cinema_id', 'name']); // Không được trùng tên phòng trong cùng rạp
$table->string('name')->nullable(); // Chỉ nullable khi thực sự cần
Schema::create('showtimes', function (Blueprint $table) {
$table->id();
$table->foreignId('movie_id')->constrained()->onDelete('restrict');
$table->foreignId('room_id')->constrained()->onDelete('restrict');
$table->date('date');
$table->time('start_time');
$table->time('end_time');
$table->decimal('price', 10, 2);
$table->enum('status', ['scheduled', 'ongoing', 'completed', 'cancelled'])->default('scheduled');
$table->timestamps();
$table->softDeletes();
// Indexes BẮT BUỘC
$table->index('movie_id');
$table->index('room_id');
$table->index('date');
$table->index('status');
$table->index(['date', 'start_time']);
$table->index(['movie_id', 'date']);
$table->index('deleted_at');
// UNIQUE constraint
$table->unique(['room_id', 'date', 'start_time']);
});
X-Api-Key: your-api-key-here
Language: vi
hoặc
Language: en
Thứ tự middleware áp dụng:
LanguageMiddleware → ApiKeyMiddleware → JWT → Role → Permission
LanguageMiddleware PHẢI chạy trước ApiKeyMiddleware để đảm bảo message error được dịch đúng ngôn ngữ khi API key không hợp lệ.
{
"success": true,
"code": "USER_CREATED_SUCCESS",
"message": "User created successfully",
"data": {
"id": 10,
"name": "Nguyễn Văn A"
}
}
{
"success": false,
"code": "EMAIL_EXISTS",
"message": "The email has already been taken",
"errors": {
"email": "This email is already in use"
}
}
code: Cả success và error đều phải có coderesources/lang/{locale}/errors.php hoặc success.phpapp/Http/Resources/namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'address' => $this->address,
'role' => new RoleResource($this->whenLoaded('role')),
'avatar' => new MediaFileResource($this->whenLoaded('avatar')),
'created_at' => $this->created_at?->toDateTimeString(),
'updated_at' => $this->updated_at?->toDateTimeString(),
];
}
}
use App\Http\Traits\ApiResponseTrait;
use App\Http\Resources\UserResource;
class AuthController extends Controller
{
use ApiResponseTrait;
public function register(Request $request)
{
// ... logic tạo user
return $this->successResponse(
'USER_CREATED_SUCCESS',
new UserResource($user),
'User created successfully'
);
}
public function me()
{
$user = auth()->user();
$user->load(['role', 'avatar']); // Eager load relationships
return $this->successResponse(
'USER_FETCHED_SUCCESS',
new UserResource($user),
'User fetched successfully'
);
}
}
app/Services/
├── Auth/
│ └── AuthService.php
├── Movie/
│ ├── MovieService.php
│ └── MovieSearchService.php
├── Booking/
│ ├── BookingService.php
│ └── BookingValidationService.php
├── Cinema/
│ ├── CinemaService.php
│ ├── RoomService.php
│ └── SeatService.php
└── ...
namespace App\Services\Movie;
use App\Models\Movie;
use Illuminate\Support\Facades\DB;
class MovieService
{
public function getAllMovies(array $filters = [])
{
$query = Movie::query();
if (isset($filters['status'])) {
$query->where('status', $filters['status']);
}
if (isset($filters['search'])) {
$query->where('title', 'like', '%' . $filters['search'] . '%');
}
return $query->with(['poster', 'trailer'])->paginate(15);
}
public function createMovie(array $data): Movie
{
return DB::transaction(function () use ($data) {
return Movie::create($data);
});
}
}
$fillable, $casts, $hidden phù hợpApiResponseTrait để trả về responseindex, store, show, update, destroy// config/api.php
return [
'middleware' => [
'api.key' => \App\Http\Middleware\ApiKeyMiddleware::class,
],
];
// config/api.php
use App\Http\Middleware\ApiKeyMiddleware;
return [
'middleware' => [
'api.key' => ApiKeyMiddleware::class,
],
];
Quy tắc:
use statement ở đầu file configdoc/api/ (ví dụ: doc/api/auth-otp.md)doc/html/ (ví dụ: doc/html/auth-otp.html) - Convert từ Markdown với dark theme, format đẹp để hiển thị trên webSau khi hoàn thành 1 task, ghi lại trong doc/task/:
Format: YYYY-MM-DD-[tên-task].md
Ví dụ: 2024-01-15-implement-authentication-api.md
app/Http/Resources/ để format dữ liệuApiResponseTrait cho responsedoc/api/ (theo format chuẩn với bảng Request Parameters và Response Fields)doc/html/ (dark theme, format đẹp)doc/task/ (theo format chuẩn)__('Text') để dịch (chuẩn Laravel)resources/lang/vi/vi.json và resources/lang/en/en.json nếu cầnuse statement trong config files, không dùng full namespace stringuse statement__())b.sql