Ver Fonte

feat:后端-项目管理

刘学玺 há 4 meses atrás
pai
commit
c65bb587ca

+ 268 - 71
app/Admin/Controllers/ProjectController.php

@@ -3,83 +3,280 @@
 namespace App\Admin\Controllers;
 
 use App\Services\ProjectService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
 use Slowlyo\OwlAdmin\Controllers\AdminController;
 
 /**
+ * @group 后台
  * 项目服务
  *
  * @property ProjectService $service
  */
 class ProjectController extends AdminController
 {
-	protected string $serviceName = ProjectService::class;
-
-	public function list()
-	{
-		$crud = $this->baseCRUD()
-			->filterTogglable(false)
-			->headerToolbar([
-				$this->createButton('dialog'),
-				...$this->baseHeaderToolBar()
-			])
-			->columns([
-				amis()->TableColumn('id', 'ID')->sortable(),
-				amis()->TableColumn('cate_id', '项目分类编号'),
-				amis()->TableColumn('cover', '项目封面'),
-				amis()->TableColumn('title', '项目标题'),
-				amis()->TableColumn('subtitle', '项目副标题'),
-				amis()->TableColumn('price', '项目金额'),
-				amis()->TableColumn('original_price', '项目原价'),
-				amis()->TableColumn('sales', '虚拟销量')->sortable(),
-				amis()->TableColumn('duration', '服务时长')->sortable(),
-				amis()->TableColumn('project_desc', '项目介绍'),
-				amis()->TableColumn('service_desc', '服务说明'),
-				amis()->TableColumn('type', '服务类型')->sortable(),
-				amis()->TableColumn('state', '状态')->sortable(),
-				amis()->TableColumn('created_at', admin_trans('admin.created_at'))->type('datetime')->sortable(),
-				amis()->TableColumn('updated_at', admin_trans('admin.updated_at'))->type('datetime')->sortable(),
-				$this->rowActions('dialog')
-			]);
-
-		return $this->baseList($crud);
-	}
-
-	public function form($isEdit = false)
-	{
-		return $this->baseForm()->body([
-			amis()->TextControl('cate_id', '项目分类编号'),
-			amis()->TextControl('cover', '项目封面'),
-			amis()->TextControl('title', '项目标题'),
-			amis()->TextControl('subtitle', '项目副标题'),
-			amis()->TextControl('price', '项目金额'),
-			amis()->TextControl('original_price', '项目原价'),
-			amis()->TextControl('sales', '虚拟销量'),
-			amis()->TextControl('duration', '服务时长'),
-			amis()->TextControl('project_desc', '项目介绍'),
-			amis()->TextControl('service_desc', '服务说明'),
-			amis()->TextControl('type', '服务类型'),
-			amis()->TextControl('state', '状态'),
-		]);
-	}
-
-	public function detail()
-	{
-		return $this->baseDetail()->body([
-			amis()->TextControl('id', 'ID')->static(),
-			amis()->TextControl('cate_id', '项目分类编号')->static(),
-			amis()->TextControl('cover', '项目封面')->static(),
-			amis()->TextControl('title', '项目标题')->static(),
-			amis()->TextControl('subtitle', '项目副标题')->static(),
-			amis()->TextControl('price', '项目金额')->static(),
-			amis()->TextControl('original_price', '项目原价')->static(),
-			amis()->TextControl('sales', '虚拟销量')->static(),
-			amis()->TextControl('duration', '服务时长')->static(),
-			amis()->TextControl('project_desc', '项目介绍')->static(),
-			amis()->TextControl('service_desc', '服务说明')->static(),
-			amis()->TextControl('type', '服务类型')->static(),
-			amis()->TextControl('state', '状态')->static(),
-			amis()->TextControl('created_at', admin_trans('admin.created_at'))->static(),
-			amis()->TextControl('updated_at', admin_trans('admin.updated_at'))->static(),
-		]);
-	}
-}
+    protected string $serviceName = ProjectService::class;
+
+    public function list()
+    {
+        $crud = $this->baseCRUD()
+            ->filterTogglable(false)
+            ->headerToolbar([
+                $this->createButton('dialog'),
+                ...$this->baseHeaderToolBar(),
+            ])
+            ->columns([
+                amis()->TableColumn('id', 'ID')->sortable(),
+                amis()->TableColumn('cate_id', '项目分类编号'),
+                amis()->TableColumn('cover', '项目封面'),
+                amis()->TableColumn('title', '项目标题'),
+                amis()->TableColumn('subtitle', '项目副标题'),
+                amis()->TableColumn('price', '项目金额'),
+                amis()->TableColumn('original_price', '项目原价'),
+                amis()->TableColumn('sales', '虚拟销量')->sortable(),
+                amis()->TableColumn('duration', '服务时长')->sortable(),
+                amis()->TableColumn('project_desc', '项目介绍'),
+                amis()->TableColumn('service_desc', '服务说明'),
+                amis()->TableColumn('type', '服务类型')->sortable(),
+                amis()->TableColumn('state', '状态')->sortable(),
+                amis()->TableColumn('created_at', admin_trans('admin.created_at'))->type('datetime')->sortable(),
+                amis()->TableColumn('updated_at', admin_trans('admin.updated_at'))->type('datetime')->sortable(),
+                $this->rowActions('dialog'),
+            ]);
+
+        return $this->baseList($crud);
+    }
+
+    public function form($isEdit = false)
+    {
+        return $this->baseForm()->body([
+            amis()->TextControl('cate_id', '项目分类编号'),
+            amis()->TextControl('cover', '项目封面'),
+            amis()->TextControl('title', '项目标题'),
+            amis()->TextControl('subtitle', '项目副标题'),
+            amis()->TextControl('price', '项目金额'),
+            amis()->TextControl('original_price', '项目原价'),
+            amis()->TextControl('sales', '虚拟销量'),
+            amis()->TextControl('duration', '服务时长'),
+            amis()->TextControl('project_desc', '项目介绍'),
+            amis()->TextControl('service_desc', '服务说明'),
+            amis()->TextControl('type', '服务类型'),
+            amis()->TextControl('state', '状态'),
+        ]);
+    }
+
+    public function detail()
+    {
+        return $this->baseDetail()->body([
+            amis()->TextControl('id', 'ID')->static(),
+            amis()->TextControl('cate_id', '项目分类编号')->static(),
+            amis()->TextControl('cover', '项目封面')->static(),
+            amis()->TextControl('title', '项目标题')->static(),
+            amis()->TextControl('subtitle', '项目副标题')->static(),
+            amis()->TextControl('price', '项目金额')->static(),
+            amis()->TextControl('original_price', '项目原价')->static(),
+            amis()->TextControl('sales', '虚拟销量')->static(),
+            amis()->TextControl('duration', '服务时长')->static(),
+            amis()->TextControl('project_desc', '项目介绍')->static(),
+            amis()->TextControl('service_desc', '服务说明')->static(),
+            amis()->TextControl('type', '服务类型')->static(),
+            amis()->TextControl('state', '状态')->static(),
+            amis()->TextControl('created_at', admin_trans('admin.created_at'))->static(),
+            amis()->TextControl('updated_at', admin_trans('admin.updated_at'))->static(),
+        ]);
+    }
+
+    /**
+     * [项目管理]获取项目列表
+     *
+     * @description 获取项目列表,支持分页和搜索
+     *
+     * @queryParam page int 页码 Example: 1
+     * @queryParam per_page int 每页数量 Example: 10
+     * @queryParam keyword string 搜索关键词 Example: null
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "success",
+     *  "data": {
+     *    "items": [],
+     *    "total": 0
+     *  }
+     * }
+     */
+    public function getProjects(Request $request): JsonResponse
+    {
+        $result = $this->service->getProjects(
+            $request->input('page', 1),
+            $request->input('per_page', 10),
+            $request->input('keyword')
+        );
+
+        return response()->json([
+            'code' => 0,
+            'message' => 'success',
+            'data' => $result,
+        ]);
+    }
+
+    /**
+     * [项目管理]创建项目
+     *
+     * @description 创建新的项目
+     *
+     * @header x-xsrf-token string required CSRF令牌 Example: 7d9ht4EM6kGpzE9eSfSVsqC2RJRyZaBZQAnXCPB3
+     *
+     * @bodyParam title string required 项目标题 Example: 精油SPA
+     * @bodyParam subtitle string required 项目副标题 Example: 专业精油按摩
+     * @bodyParam cover string required 项目封面 Example: http://example.com/cover.jpg
+     * @bodyParam price numeric required 项目金额 Example: 299.00
+     * @bodyParam original_price numeric required 项目原价 Example: 399.00
+     * @bodyParam duration int required 服务时长(分钟) Example: 60
+     * @bodyParam project_desc string required 项目介绍 Example: 专业精油按摩服务
+     * @bodyParam service_desc string required 服务说明 Example: 服务流程说明
+     * @bodyParam type int required 服务类型 Example: 1
+     * @bodyParam state int required 状态(0:下架 1:上架) Example: 1
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "success",
+     *  "data": null
+     * }
+     */
+    public function createProject(Request $request): JsonResponse
+    {
+        // 验证请求数据
+        $validated = $request->validate([
+            'title' => 'required|string|max:100',
+            'subtitle' => 'required|string|max:200',
+            'cover' => 'required|string|max:255',
+            'price' => 'required|numeric|min:0',
+            'original_price' => 'required|numeric|min:0',
+            'duration' => 'required|integer|min:1',
+            'project_desc' => 'required|string',
+            'service_desc' => 'required|string',
+            'type' => 'required|integer|in:1,2,3', // 根据实际类型值修改
+            'state' => 'required|integer|in:0,1',
+        ]);
+
+        // 设置默认分类
+        $validated['cate_id'] = 1;
+
+        try {
+            $result = $this->service->createProject($validated);
+
+            return response()->json([
+                'code' => 0,
+                'message' => 'success',
+                'data' => $result,
+            ]);
+        } catch (\Exception $e) {
+            return response()->json([
+                'code' => 500,
+                'message' => '创建项目失败:'.$e->getMessage(),
+            ], 500);
+        }
+    }
+
+    /**
+     * [项目管理]更新项目
+     *
+     * @description 更新项目信息
+     *
+     * @header x-xsrf-token string required CSRF令牌 Example: 7d9ht4EM6kGpzE9eSfSVsqC2RJRyZaBZQAnXCPB3
+     *
+     * @bodyParam title string 项目标题 Example: 精油SPA
+     * @bodyParam subtitle string 项目副标题 Example: 专业精油按摩
+     * @bodyParam cover string 项目封面 Example: http://example.com/cover.jpg
+     * @bodyParam price numeric 项目金额 Example: 299.00
+     * @bodyParam original_price numeric 项目原价 Example: 399.00
+     * @bodyParam duration int 服务时长(分钟) Example: 60
+     * @bodyParam project_desc string 项目介绍 Example: 专业精油按摩服务
+     * @bodyParam service_desc string 服务说明 Example: 服务流程说明
+     * @bodyParam type int 服务类型 Example: 1
+     * @bodyParam state int 状态(0:下架 1:上架) Example: 1
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "success",
+     *  "data": null
+     * }
+     */
+    public function updateProject(Request $request, int $id): JsonResponse
+    {
+        // 验证请求数据
+        $validated = $request->validate([
+            'title' => 'sometimes|string|max:100',
+            'subtitle' => 'sometimes|string|max:200',
+            'cover' => 'sometimes|string|max:255',
+            'price' => 'sometimes|numeric|min:0',
+            'original_price' => 'sometimes|numeric|min:0',
+            'duration' => 'sometimes|integer|min:1',
+            'project_desc' => 'sometimes|string',
+            'service_desc' => 'sometimes|string',
+            'type' => 'sometimes|integer|in:1,2,3',
+            'state' => 'sometimes|integer|in:0,1',
+        ]);
+
+        try {
+            $result = $this->service->updateProject($id, $validated);
+
+            return response()->json([
+                'code' => 0,
+                'message' => 'success',
+                'data' => $result,
+            ]);
+        } catch (\Exception $e) {
+            return response()->json([
+                'code' => 500,
+                'message' => '更新项目失败:'.$e->getMessage(),
+            ], 500);
+        }
+    }
+
+    /**
+     * [项目管理]删除项目
+     *
+     * @description 删除指定项目
+     *
+     * @header x-xsrf-token string required CSRF令牌 Example: 7d9ht4EM6kGpzE9eSfSVsqC2RJRyZaBZQAnXCPB3
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "success",
+     *  "data": null
+     * }
+     */
+    public function deleteProject(int $id): JsonResponse
+    {
+        $result = $this->service->deleteProject($id);
+
+        return response()->json([
+            'code' => 0,
+            'message' => 'success',
+            'data' => $result,
+        ]);
+    }
+
+    /**
+     * [项目管理]获取项目详情
+     *
+     * @description 获取指定项目的详细信息
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "success",
+     *  "data": {}
+     * }
+     */
+    public function getProject(int $id): JsonResponse
+    {
+        $result = $this->service->getProject($id);
+
+        return response()->json([
+            'code' => 0,
+            'message' => 'success',
+            'data' => $result,
+        ]);
+    }
+}

+ 3 - 2
app/Http/Middleware/VerifyCsrfToken.php

@@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
      * @var array<int, string>
      */
     protected $except = [
-        'api/*'
+        'api/*',
+        'admin/project',
     ];
-} 
+}

+ 5 - 3
app/Models/Project.php

@@ -10,7 +10,9 @@ use Slowlyo\OwlAdmin\Models\BaseModel as Model;
  */
 class Project extends Model
 {
-	use SoftDeletes;
+    use SoftDeletes;
 
-	protected $table = 'project';
-}
+    protected $table = 'project';
+
+    protected $guarded = [];
+}

+ 123 - 2
app/Services/ProjectService.php

@@ -3,6 +3,8 @@
 namespace App\Services;
 
 use App\Models\Project;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 use Slowlyo\OwlAdmin\Services\AdminService;
 
 /**
@@ -13,5 +15,124 @@ use Slowlyo\OwlAdmin\Services\AdminService;
  */
 class ProjectService extends AdminService
 {
-	protected string $modelName = Project::class;
-}
+    protected string $modelName = Project::class;
+
+    /**
+     * 获取项目列表
+     */
+    public function getProjects(int $page, int $perPage, ?string $keyword = null): array
+    {
+        try {
+            $query = $this->query();
+
+            // 如果有关键词,添加搜索条件
+            if ($keyword) {
+                $query->where(function ($q) use ($keyword) {
+                    $q->where('title', 'like', "%{$keyword}%")
+                        ->orWhere('subtitle', 'like', "%{$keyword}%");
+                });
+            }
+
+            $total = $query->count();
+            $items = $query->forPage($page, $perPage)
+                ->orderBy('id', 'desc')
+                ->get();
+
+            return [
+                'items' => $items,
+                'total' => $total,
+            ];
+        } catch (\Exception $e) {
+            Log::error('获取项目列表失败', [
+                'error' => $e->getMessage(),
+                'params' => func_get_args(),
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 创建项目
+     *
+     * @return mixed
+     */
+    public function createProject(array $data)
+    {
+        try {
+            return DB::transaction(function () use ($data) {
+                // 创建项目记录
+                return $this->query()->create($data);
+            });
+        } catch (\Exception $e) {
+            Log::error('创建项目失败', [
+                'error' => $e->getMessage(),
+                'data' => $data,
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 更新项目
+     *
+     * @return mixed
+     */
+    public function updateProject(int $id, array $data)
+    {
+        try {
+            return DB::transaction(function () use ($id, $data) {
+                $project = $this->query()->findOrFail($id);
+                // 更新项目信息
+                $project->update($data);
+
+                return $project;
+            });
+        } catch (\Exception $e) {
+            Log::error('更新项目失败', [
+                'error' => $e->getMessage(),
+                'id' => $id,
+                'data' => $data,
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 删除项目
+     */
+    public function deleteProject(int $id): bool
+    {
+        try {
+            return DB::transaction(function () use ($id) {
+                $project = $this->query()->findOrFail($id);
+
+                // 删除项目
+                return $project->delete();
+            });
+        } catch (\Exception $e) {
+            Log::error('删除项目失败', [
+                'error' => $e->getMessage(),
+                'id' => $id,
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 获取项目详情
+     *
+     * @return mixed
+     */
+    public function getProject(int $id)
+    {
+        try {
+            return $this->query()->findOrFail($id);
+        } catch (\Exception $e) {
+            Log::error('获取项目详情失败', [
+                'error' => $e->getMessage(),
+                'id' => $id,
+            ]);
+            throw $e;
+        }
+    }
+}

+ 4 - 4
routes/admin.php

@@ -8,13 +8,13 @@
 
 // =====================================================================
 
-use Slowlyo\OwlAdmin\Admin;
 use Illuminate\Routing\Router;
 use Illuminate\Support\Facades\Route;
+use Slowlyo\OwlAdmin\Admin;
 
 Route::group([
-    'domain'     => Admin::config('admin.route.domain'),
-    'prefix'     => Admin::config('admin.route.prefix'),
+    'domain' => Admin::config('admin.route.domain'),
+    'prefix' => Admin::config('admin.route.prefix'),
     'middleware' => Admin::config('admin.route.middleware'),
 ], function (Router $router) {
     // 用户
@@ -100,4 +100,4 @@ Route::group([
     // 技师投流订单
     $router->resource('coach_flow_orders', \App\Admin\Controllers\CoachFlowOrderController::class);
 
-});
+});

+ 9 - 0
routes/web.php

@@ -144,4 +144,13 @@ Route::group([
         // 获取设置值详情
         Route::get('/{id}', [\App\Admin\Controllers\SettingValueController::class, 'getValueDetail']);
     });
+
+    // 项目管理路由
+    Route::prefix('projects')->group(function () {
+        Route::get('/', [App\Admin\Controllers\ProjectController::class, 'getProjects']);
+        Route::post('/', [App\Admin\Controllers\ProjectController::class, 'createProject']);
+        Route::get('/{id}', [App\Admin\Controllers\ProjectController::class, 'getProject']);
+        Route::put('/{id}', [App\Admin\Controllers\ProjectController::class, 'updateProject']);
+        Route::delete('/{id}', [App\Admin\Controllers\ProjectController::class, 'deleteProject']);
+    });
 });