[FEAT] 實作公共事業費附件上傳管理與更新 UI 協作規範防呆機制
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 55s
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 55s
This commit is contained in:
@@ -4,8 +4,10 @@ namespace App\Modules\Finance\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Modules\Finance\Models\UtilityFee;
|
||||
use App\Modules\Finance\Models\UtilityFeeAttachment;
|
||||
use App\Modules\Finance\Contracts\FinanceServiceInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class UtilityFeeController extends Controller
|
||||
@@ -103,8 +105,82 @@ class UtilityFeeController extends Controller
|
||||
->event('deleted')
|
||||
->log('deleted');
|
||||
|
||||
// 刪除實體檔案 (如果 cascade 沒處理或是想要手動清理)
|
||||
foreach ($utility_fee->attachments as $attachment) {
|
||||
Storage::disk('public')->delete($attachment->file_path);
|
||||
}
|
||||
|
||||
$utility_fee->delete();
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
/**
|
||||
* 獲取附件列表
|
||||
*/
|
||||
public function attachments(UtilityFee $utility_fee)
|
||||
{
|
||||
return response()->json([
|
||||
'attachments' => $utility_fee->attachments()->orderBy('created_at', 'desc')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上傳附件
|
||||
*/
|
||||
public function uploadAttachment(Request $request, UtilityFee $utility_fee)
|
||||
{
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:jpeg,jpg,png,webp,pdf|max:2048', // 2MB
|
||||
]);
|
||||
|
||||
// 檢查數量限制 (最多 3 張)
|
||||
if ($utility_fee->attachments()->count() >= 3) {
|
||||
return response()->json(['message' => '附件數量已達上限 (最多 3 個)'], 422);
|
||||
}
|
||||
|
||||
$file = $request->file('file');
|
||||
$path = $file->store("utility-fee-attachments/{$utility_fee->id}", 'public');
|
||||
|
||||
$attachment = $utility_fee->attachments()->create([
|
||||
'file_path' => $path,
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'mime_type' => $file->getMimeType(),
|
||||
'size' => $file->getSize(),
|
||||
]);
|
||||
|
||||
activity()
|
||||
->performedOn($utility_fee)
|
||||
->causedBy(auth()->user())
|
||||
->event('attachment_uploaded')
|
||||
->log("uploaded attachment: {$attachment->original_name}");
|
||||
|
||||
return response()->json([
|
||||
'message' => '上傳成功',
|
||||
'attachment' => $attachment
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刪除附件
|
||||
*/
|
||||
public function deleteAttachment(UtilityFee $utility_fee, UtilityFeeAttachment $attachment)
|
||||
{
|
||||
// 確保附件屬於該費用
|
||||
if ($attachment->utility_fee_id !== $utility_fee->id) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
Storage::disk('public')->delete($attachment->file_path);
|
||||
|
||||
$attachment->delete();
|
||||
|
||||
activity()
|
||||
->performedOn($utility_fee)
|
||||
->causedBy(auth()->user())
|
||||
->event('attachment_deleted')
|
||||
->log("deleted attachment: {$attachment->original_name}");
|
||||
|
||||
return response()->json(['message' => '刪除成功']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,16 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UtilityFee extends Model
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UtilityFeeFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* 此公共事業費的附件
|
||||
*/
|
||||
public function attachments()
|
||||
{
|
||||
return $this->hasMany(UtilityFeeAttachment::class);
|
||||
}
|
||||
|
||||
// 狀態常數
|
||||
const STATUS_PENDING = 'pending';
|
||||
const STATUS_PAID = 'paid';
|
||||
|
||||
36
app/Modules/Finance/Models/UtilityFeeAttachment.php
Normal file
36
app/Modules/Finance/Models/UtilityFeeAttachment.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Finance\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UtilityFeeAttachment extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'utility_fee_id',
|
||||
'file_path',
|
||||
'original_name',
|
||||
'mime_type',
|
||||
'size',
|
||||
];
|
||||
|
||||
protected $appends = ['url'];
|
||||
|
||||
/**
|
||||
* 附件所属的公共事業費
|
||||
*/
|
||||
public function utilityFee(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UtilityFee::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 獲取附件的全路徑 URL
|
||||
*/
|
||||
public function getUrlAttribute()
|
||||
{
|
||||
return tenant_asset($this->file_path);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,11 @@ Route::middleware('auth')->group(function () {
|
||||
});
|
||||
Route::middleware('permission:utility_fees.edit')->group(function () {
|
||||
Route::put('/utility-fees/{utility_fee}', [UtilityFeeController::class, 'update'])->name('utility-fees.update');
|
||||
|
||||
// 附件管理 (Ajax)
|
||||
Route::get('/utility-fees/{utility_fee}/attachments', [UtilityFeeController::class, 'attachments'])->name('utility-fees.attachments');
|
||||
Route::post('/utility-fees/{utility_fee}/attachments', [UtilityFeeController::class, 'uploadAttachment'])->name('utility-fees.upload-attachment');
|
||||
Route::delete('/utility-fees/{utility_fee}/attachments/{attachment}', [UtilityFeeController::class, 'deleteAttachment'])->name('utility-fees.delete-attachment');
|
||||
});
|
||||
Route::middleware('permission:utility_fees.delete')->group(function () {
|
||||
Route::delete('/utility-fees/{utility_fee}', [UtilityFeeController::class, 'destroy'])->name('utility-fees.destroy');
|
||||
|
||||
@@ -67,7 +67,7 @@ class FinanceService implements FinanceServiceInterface
|
||||
|
||||
public function getUtilityFees(array $filters)
|
||||
{
|
||||
$query = UtilityFee::query();
|
||||
$query = UtilityFee::withCount('attachments');
|
||||
|
||||
if (!empty($filters['search'])) {
|
||||
$search = $filters['search'];
|
||||
|
||||
Reference in New Issue
Block a user