update
This commit is contained in:
@@ -244,10 +244,22 @@ class AdminService implements AdminServiceInterface
|
||||
$user->card += $diff;
|
||||
$user->save();
|
||||
|
||||
$targetRecord = AddCard::where('buyer', $addCard->buyer)
|
||||
->where('seller', $data['seller'])
|
||||
->whereDate('date', $data['date'])
|
||||
->where('id', '!=', $addCard->id)
|
||||
->first();
|
||||
|
||||
if ($targetRecord) {
|
||||
$targetRecord->num_card += $newNumCard;
|
||||
$targetRecord->save();
|
||||
$addCard->delete();
|
||||
} else {
|
||||
$addCard->num_card = $newNumCard;
|
||||
$addCard->seller = $data['seller'];
|
||||
$addCard->date = $data['date'];
|
||||
$addCard->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+65
-12
@@ -1,28 +1,81 @@
|
||||
# Hướng dẫn thiết lập Cron Job cho Laravel trên Ubuntu Server
|
||||
# Hướng dẫn thiết lập Cron Job cho Laravel trên Server (Ubuntu)
|
||||
|
||||
Để hệ thống tự động chạy các tác vụ định kỳ (bao gồm tác vụ tự động reset thẻ vào cuối tháng qua lệnh `cards:reset`), bạn cần cấu hình Cron Job trên hệ điều hành Ubuntu Server trỏ đến bộ lập lịch (Task Scheduler) của Laravel.
|
||||
Để hệ thống tự động thực hiện các tác vụ định kỳ (bao gồm việc tự động reset thẻ vào lúc 23:59 ngày cuối tháng thông qua lệnh `cards:reset`), bạn cần cấu hình Cron Job trên hệ điều hành Ubuntu Server.
|
||||
|
||||
## Các bước thiết lập
|
||||
Dưới đây là hướng dẫn chi tiết cho cả hai trường hợp: **Chạy qua Docker Compose** (mặc định của dự án) và **Chạy trực tiếp trên Host (Không dùng Docker)**.
|
||||
|
||||
1. **Mở cấu hình Crontab:**
|
||||
Truy cập vào terminal của máy chủ Ubuntu dưới quyền của user chạy web (thường là `www-data` hoặc user deploy của bạn) và chạy lệnh:
|
||||
---
|
||||
|
||||
## TRƯỜNG HỢP 1: Chạy qua Docker Compose (Khuyên dùng cho dự án này)
|
||||
|
||||
Vì dự án chạy trong container Docker, Cron Job trên server Ubuntu (máy Host) cần gọi lệnh của Laravel vào bên trong container `app`.
|
||||
|
||||
### Các bước thiết lập:
|
||||
|
||||
1. **Mở file cấu hình Crontab của Ubuntu:**
|
||||
Mở terminal của Ubuntu Server và chạy lệnh:
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
2. **Thêm cấu hình chạy Scheduler:**
|
||||
Thêm dòng dưới đây vào cuối file cấu hình crontab để gọi Laravel scheduler kiểm tra tác vụ mỗi phút:
|
||||
2. **Thêm cấu hình chạy Laravel Scheduler:**
|
||||
Thêm dòng dưới đây vào cuối file crontab:
|
||||
```bash
|
||||
* * * * * cd /đường-dẫn-đến-thư-mục-dự-án && php artisan schedule:run >> /dev/null 2>&1
|
||||
* * * * * cd /đường-dẫn-đến-thư-mục-dự-án && docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:run >> /dev/null 2>&1
|
||||
```
|
||||
*Lưu ý:* Thay thế `/đường-dẫn-đến-thư-mục-dự-án` bằng đường dẫn tuyệt đối đến thư mục chứa dự án trên server (Ví dụ: `/var/www/thankcard-system`).
|
||||
*Lưu ý quan trọng:*
|
||||
- Thay `/đường-dẫn-đến-thư-mục-dự-án` thành đường dẫn tuyệt đối đến dự án trên server (Ví dụ: `/var/www/thankcard-system`).
|
||||
- Tham số **`-T`** (trong `exec -T`) là bắt buộc để tắt chế độ TTY (Tương tác nhập liệu), giúp cron chạy ngầm thành công mà không bị lỗi TTY error.
|
||||
|
||||
3. **Lưu và Thoát:**
|
||||
- Nếu bạn dùng trình soạn thảo `nano`: Nhấn `Ctrl + O` -> nhấn `Enter` để lưu, sau đó bấm `Ctrl + X` để thoát.
|
||||
- Hệ thống sẽ hiển thị thông báo `crontab: installing new crontab`.
|
||||
|
||||
4. **Kiểm tra trạng thái:**
|
||||
Chạy lệnh sau để hiển thị danh sách các cron job đang hoạt động trên user đó:
|
||||
---
|
||||
|
||||
## TRƯỜNG HỢP 2: Chạy trực tiếp trên Ubuntu Host (Nếu deploy dạng truyền thống)
|
||||
|
||||
Nếu bạn cấu hình chạy PHP và Web Server (Nginx/Apache) trực tiếp trên Ubuntu mà không qua Docker.
|
||||
|
||||
### Các bước thiết lập:
|
||||
|
||||
1. **Mở cấu hình Crontab:**
|
||||
Truy cập terminal của Ubuntu Server dưới quyền của user chạy Web (thường là `www-data` hoặc user deploy) và chạy lệnh:
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
2. **Thêm cấu hình chạy Laravel Scheduler:**
|
||||
Thêm dòng dưới đây vào cuối file crontab:
|
||||
```bash
|
||||
* * * * * cd /đường-dẫn-đến-thư-mục-dự-án && php artisan schedule:run >> /dev/null 2>&1
|
||||
```
|
||||
*Lưu ý:* Thay `/đường-dẫn-đến-thư-mục-dự-án` bằng đường dẫn tuyệt đối đến thư mục chứa dự án.
|
||||
|
||||
3. **Lưu và Thoát.**
|
||||
|
||||
---
|
||||
|
||||
## Hướng dẫn kiểm tra trạng thái Cron Job
|
||||
|
||||
1. **Xem danh sách cron job đang hoạt động:**
|
||||
```bash
|
||||
crontab -l
|
||||
```
|
||||
|
||||
2. **Kiểm tra danh sách các tác vụ đã lên lịch trong Laravel:**
|
||||
```bash
|
||||
# Nếu dùng Docker:
|
||||
docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:list
|
||||
|
||||
# Nếu không dùng Docker:
|
||||
php artisan schedule:list
|
||||
```
|
||||
|
||||
3. **Chạy thử scheduler ngay lập tức bằng tay để kiểm thử:**
|
||||
```bash
|
||||
# Nếu dùng Docker:
|
||||
docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:run
|
||||
|
||||
# Nếu không dùng Docker:
|
||||
php artisan schedule:run
|
||||
```
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
<div class="w-16 h-16 rounded-full overflow-hidden border border-gray-200 shrink-0">
|
||||
<img src="{{ ($user && $user->avatar) ? asset($user->avatar) : asset('images/avatar.png') }}" alt="Avatar" class="w-full h-full object-cover" id="avatar-preview" onerror="this.onerror=null; this.src='{{ asset('images/avatar.png') }}';">
|
||||
</div>
|
||||
@if($mode !== 'edit')
|
||||
<div class="flex-1">
|
||||
<label class="block text-[13px] font-bold text-gray-700 mb-1" for="avatar">Thay đổi ảnh đại diện</label>
|
||||
<input type="file" name="avatar" id="avatar" accept="image/*" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-3 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-[#3462f7] file:text-white hover:file:bg-[#254edb]" onchange="previewAvatar(event)">
|
||||
@error('avatar')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($mode !== 'edit')
|
||||
@once
|
||||
@push('scripts')
|
||||
<script>
|
||||
@@ -27,3 +30,4 @@
|
||||
</script>
|
||||
@endpush
|
||||
@endonce
|
||||
@endif
|
||||
|
||||
@@ -69,11 +69,6 @@
|
||||
Bạn có chắc chắn muốn cho nhân viên {{ $user->msnv }} nghỉ việc không? Tài khoản này sẽ không thể đăng nhập hoặc thực hiện các giao dịch thẻ sau khi xác nhận.
|
||||
</p>
|
||||
@else
|
||||
<!-- 1. User Avatar Section -->
|
||||
@if($isSelf || $isEdit)
|
||||
<x-user-avatar-section :mode="$mode" :user="$user" />
|
||||
@endif
|
||||
|
||||
<!-- 2. User Basic Information Section -->
|
||||
<x-user-basic-info :mode="$mode" :user="$user" />
|
||||
|
||||
|
||||
@@ -743,7 +743,7 @@ class AdminUserListStatsTest extends TestCase
|
||||
'buyer' => 9002,
|
||||
'num_card' => 5,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->format('Y-m-d'),
|
||||
'date' => Carbon::now()->startOfMonth()->toDateString(),
|
||||
]);
|
||||
|
||||
// Transaction 2 (newest)
|
||||
@@ -751,14 +751,14 @@ class AdminUserListStatsTest extends TestCase
|
||||
'buyer' => 9002,
|
||||
'num_card' => 10,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->format('Y-m-d'),
|
||||
'date' => Carbon::now()->startOfMonth()->addDay()->toDateString(),
|
||||
]);
|
||||
|
||||
// Try to update older Transaction 1: change num_card from 5 to 6 (increase of 1)
|
||||
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $oldAddCard->id), [
|
||||
'num_card' => 6,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->format('Y-m-d'),
|
||||
'date' => Carbon::now()->startOfMonth()->toDateString(),
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
@@ -771,6 +771,72 @@ class AdminUserListStatsTest extends TestCase
|
||||
$this->assertEquals(16, $member->card);
|
||||
}
|
||||
|
||||
public function test_update_add_card_merges_records_on_same_date(): void
|
||||
{
|
||||
$admin = User::create([
|
||||
'msnv' => 9001,
|
||||
'name' => 'Admin User',
|
||||
'mail' => 'admin@example.com',
|
||||
'pass' => md5('password'),
|
||||
'departments' => 1,
|
||||
'role' => config('constants.ROLE_ADMIN'),
|
||||
'status' => config('constants.STATUS_ACTIVE'),
|
||||
'card' => 0,
|
||||
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
|
||||
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
|
||||
]);
|
||||
|
||||
$member = User::create([
|
||||
'msnv' => 9002,
|
||||
'name' => 'Member User',
|
||||
'mail' => 'member@example.com',
|
||||
'pass' => md5('password'),
|
||||
'departments' => 1,
|
||||
'role' => config('constants.ROLE_MEMBER'),
|
||||
'status' => config('constants.STATUS_ACTIVE'),
|
||||
'card' => 15,
|
||||
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
|
||||
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
|
||||
]);
|
||||
|
||||
// Record A on day 1
|
||||
$recordA = \App\Models\AddCard::create([
|
||||
'buyer' => 9002,
|
||||
'num_card' => 5,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->startOfMonth()->toDateString(),
|
||||
]);
|
||||
|
||||
// Record B on day 10
|
||||
$recordB = \App\Models\AddCard::create([
|
||||
'buyer' => 9002,
|
||||
'num_card' => 10,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->startOfMonth()->addDays(9)->toDateString(),
|
||||
]);
|
||||
|
||||
// Update Record B: set date to day 1, and set num_card to 10
|
||||
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $recordB->id), [
|
||||
'num_card' => 10,
|
||||
'seller' => 9001,
|
||||
'date' => Carbon::now()->startOfMonth()->toDateString(),
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHasNoErrors();
|
||||
|
||||
// Record B should be deleted (merged)
|
||||
$this->assertNull(\App\Models\AddCard::find($recordB->id));
|
||||
|
||||
// Record A should have 15 cards (5 + 10)
|
||||
$recordA->refresh();
|
||||
$this->assertEquals(15, $recordA->num_card);
|
||||
|
||||
// Member's card balance remains 15
|
||||
$member->refresh();
|
||||
$this->assertEquals(15, $member->card);
|
||||
}
|
||||
|
||||
public function test_delete_add_card_successfully_updates_balance(): void
|
||||
{
|
||||
$admin = User::create([
|
||||
|
||||
Reference in New Issue
Block a user