Browse Source

feat: 添加订单评级功能,包括验证和模型更新
-在“OrderController”中实现了一种新的“rate”方法,供用户对订单进行评分。
-创建了“CommentTag”模型来管理评级标签。
-更新了“订单”和“订单评论”模型,以支持订单评级和评论。
-增强的“OrderService”具有“rateOrder”方法,用于处理评级逻辑和更新订单状态。
-为订单评级功能添加了API路由。

刘学玺 3 months ago
parent
commit
1e57ed616e

+ 53 - 0
app/Http/Controllers/Client/OrderController.php

@@ -394,4 +394,57 @@ class OrderController extends Controller
     {
         return $this->success($this->service->generateVerificationCode(Auth::user()->id, $id));
     }
+
+    /**
+     * [订单]订单评价
+     *
+     * 用户评价订单
+     *
+     * @authenticated
+     *
+     * @bodyParam order_id int required 订单ID
+     * @bodyParam service_score decimal 服务评分(1-5分,支持一位小数) Example: 4.5
+     * @bodyParam appearance_score decimal 形象评分(1-5分,支持一位小数) Example: 4.5
+     * @bodyParam attitude_score decimal 态度评分(1-5分,支持一位小数) Example: 4.5
+     * @bodyParam professional_score decimal 专业评分(1-5分,支持一位小数) Example: 4.5
+     * @bodyParam tags array 评价标签ID数组 Example: [1,2,3]
+     * @bodyParam content string 评价内容 Example: 服务很专业,技师态度很好
+     * @bodyParam images array 评价图片数组 Example: ["path/to/image1.jpg", "path/to/image2.jpg"]
+     *
+     * @response 200 {
+     *     "code": 200,
+     *     "message": "评价成功",
+     *     "data": null
+     * }
+     * @response 400 {
+     *     "code": 400,
+     *     "message": "订单不存在",
+     *     "data": null
+     * }
+     * @response 422 {
+     *     "code": 422,
+     *     "message": "评分必须在1-5分之间",
+     *     "data": null
+     * }
+     *
+     * @throws \Exception
+     */
+    public function rate(Request $request)
+    {
+        $validated = $request->validate([
+            'order_id' => 'required|integer',
+            'service_score' => 'nullable|numeric|min:1|max:5',
+            'appearance_score' => 'nullable|numeric|min:1|max:5',
+            'attitude_score' => 'nullable|numeric|min:1|max:5',
+            'professional_score' => 'nullable|numeric|min:1|max:5',
+            'tags' => 'nullable|array',
+            'tags.*' => 'integer|exists:comment_tags,id',
+            'content' => 'nullable|string|max:1000',
+            'images' => 'nullable|array',
+            'images.*' => 'string'
+        ]);
+
+        $this->service->rateOrder(Auth::user()->id, $validated);
+        return $this->success(null, '评价成功');
+    }
 }

+ 23 - 0
app/Models/CommentTag.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class CommentTag extends Model
+{
+    protected $table = 'comment_tags';
+
+    protected $fillable = [
+        'name',
+        'type',
+        'sort',
+        'status'
+    ];
+
+    protected $casts = [
+        'type' => 'integer',
+        'sort' => 'integer',
+        'status' => 'integer'
+    ];
+}

+ 8 - 0
app/Models/Order.php

@@ -78,4 +78,12 @@ class Order extends Model
     {
         return $this->hasOne(OrderComment::class, 'order_id', 'id');
     }
+
+    /**
+     * 获取订单评价
+     */
+    public function comments()
+    {
+        return $this->hasOne(OrderComment::class);
+    }
 }

+ 36 - 22
app/Models/OrderComment.php

@@ -2,51 +2,65 @@
 
 namespace App\Models;
 
-use Illuminate\Database\Eloquent\SoftDeletes;
-use Slowlyo\OwlAdmin\Models\BaseModel as Model;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
-/**
- * 订单评论
- */
 class OrderComment extends Model
 {
-    use SoftDeletes;
+    protected $fillable = [
+        'order_id',
+        'user_id',
+        'coach_id',
+        'service_score',
+        'appearance_score',
+        'attitude_score',
+        'professional_score',
+        'tags',
+        'content',
+        'images',
+    ];
 
-    protected $table = 'order_comments';
+    protected $casts = [
+        'tags' => 'array',
+        'images' => 'array',
+        'service_score' => 'float',
+        'appearance_score' => 'float',
+        'attitude_score' => 'float',
+        'professional_score' => 'float',
+    ];
 
     /**
-     * @Author FelixYin
-     * @description 评论所属订单
+     * 关联订单
      */
-    public function order()
+    public function order(): BelongsTo
     {
-        return $this->belongsTo('App\Models\Order', 'order_id');
+        return $this->belongsTo(Order::class);
     }
 
+
     /**
-     * @Author FelixYin
-     * @description 评论所属会员
+     * 关联用户
      */
-    public function member()
+    public function user(): BelongsTo
     {
-        return $this->belongsTo(MemberUser::class, 'user_id', 'id');
+        return $this->belongsTo(MemberUser::class, 'user_id');
     }
 
+
     /**
      * @Author FelixYin
-     * @description 评论所属技师
+     * @description 评论所属会员
      */
-    public function coach()
+    public function member()
     {
-        return $this->belongsTo(CoachUser::class, 'coach_id', 'id');
+        return $this->belongsTo(MemberUser::class, 'user_id', 'id');
     }
 
     /**
-     * @Author FelixYin
-     * @description 评论所属订单
+     * 关联技师
      */
-    public function basicOrder()
+    public function coach(): BelongsTo
     {
-        return $this->belongsTo(Order::class, 'order_id', 'id');
+        return $this->belongsTo(CoachUser::class, 'coach_id');
     }
 }

+ 106 - 0
app/Services/Client/OrderService.php

@@ -2761,4 +2761,110 @@ readonly class OrderService
 
         return $order;
     }
+
+    /**
+     * 评价订单
+     *
+     * @param int $userId 用户ID
+     * @param array $data 评价数据
+     * @throws \Exception
+     */
+    public function rateOrder(int $userId, array $data): void
+    {
+        $order = Order::where('user_id', $userId)
+            ->where('id', $data['order_id'])
+            ->first();
+
+        if (!$order) {
+            throw new \Exception('订单不存在');
+        }
+
+        if ($order->status !== OrderStatus::LEFT->value) {
+            throw new \Exception('订单状态不正确');
+        }
+
+        // 验证评分范围
+        foreach (['service_score', 'appearance_score', 'attitude_score', 'professional_score'] as $scoreField) {
+            if (isset($data[$scoreField]) && ($data[$scoreField] < 1 || $data[$scoreField] > 5)) {
+                throw new \Exception('评分范围必须在1-5之间');
+            }
+        }
+
+        DB::transaction(function () use ($order, $data, $userId) {
+            // 创建评价记录
+            $comment = $order->comments()->create([
+                'user_id' => $userId,
+                'coach_id' => $order->coach_id,
+                'service_score' => $data['service_score'] ?? 5,
+                'appearance_score' => $data['appearance_score'] ?? 5,
+                'attitude_score' => $data['attitude_score'] ?? 5,
+                'professional_score' => $data['professional_score'] ?? 5,
+                'tags' => $data['tags'] ?? [],
+                'content' => $data['content'] ?? '',
+                'images' => $data['images'] ?? [],
+            ]);
+
+            // 更新订单状态为已完成
+            $order->update(['status' => OrderStatus::COMPLETED->value]);
+
+            // 更新技师评分
+            // $this->updateCoachScore($order->coach_id);
+
+            // 记录订单状态变更
+            OrderRecord::create([
+                'order_id' => $order->id,
+                'object_id' => $userId,
+                'object_type' => MemberUser::class,
+                'state' => OrderRecordStatus::EVALUATED->value,
+                'remark' => '用户完成评价',
+            ]);
+        });
+    }
+
+    /**
+     * 更新技师评分
+     *
+     * @param int $coachId 技师ID
+     */
+    private function updateCoachScore(int $coachId): void
+    {
+        $coach = CoachUser::find($coachId);
+        if (!$coach) {
+            return;
+        }
+
+        // 计算技师的各维度平均分
+        $avgScores = DB::table('order_comments')
+            ->where('coach_id', $coachId)
+            ->whereExists(function ($query) {
+                $query->select(DB::raw(1))
+                    ->from('orders')
+                    ->whereColumn('order_comments.order_id', 'orders.id')
+                    ->where('orders.status', OrderStatus::COMPLETED->value);
+            })
+            ->select([
+                DB::raw('AVG(service_score) as avg_service_score'),
+                DB::raw('AVG(appearance_score) as avg_appearance_score'),
+                DB::raw('AVG(attitude_score) as avg_attitude_score'),
+                DB::raw('AVG(professional_score) as avg_professional_score')
+            ])
+            ->first();
+
+        if ($avgScores) {
+            // 计算总体平均分
+            $overallScore = round(($avgScores->avg_service_score +
+                $avgScores->avg_appearance_score +
+                $avgScores->avg_attitude_score +
+                $avgScores->avg_professional_score) / 4, 1);
+
+            // 更新技师评分
+            $coach->update([
+                'score' => $overallScore,
+                'service_score' => round($avgScores->avg_service_score, 1),
+                'appearance_score' => round($avgScores->avg_appearance_score, 1),
+                'attitude_score' => round($avgScores->avg_attitude_score, 1),
+                'professional_score' => round($avgScores->avg_professional_score, 1)
+            ]);
+        }
+    }
 }

+ 3 - 0
routes/api.php

@@ -142,6 +142,9 @@ Route::prefix('client')->group(function () {
                 ->name('assign-coach');
             // 生成订单核销码
             Route::get('{id}/code', [OrderController::class, 'generateCode']);
+
+            // 评价
+            Route::post('rate', [OrderController::class, 'rate'])->name('client.order.rate');
         });
 
         // 团队管理路由