['bail', 'required', 'max:255'], 'planable_type' => [ 'bail', 'required', Rule::in([ (new PlanHygiene())->getMorphClass(), (new PlanPerformance())->getMorphClass(), ]), ], ], attributes: [ 'name' => __('plan.plan.name'), 'planable_type' => __('plan.plan.type'), ], ); switch (Relation::getMorphedModel($data['planable_type'])) { // 清洁卫生 case PlanHygiene::class: $payload = $data['plan_hygiene'] ?? []; Validator::validate( data: $payload, rules: [ 'month' => ['bail', 'required', 'date_format:Y-m'], ], attributes: [ 'month' => __('plan.plan_hygiene.month'), ], ); $planable = PlanHygiene::create($payload); $planable->plan()->create([ 'name' => $data['name'], 'plan_status' => PlanStatus::Pending, ]); break; // 业绩指标 case PlanPerformance::class: $payload = $data['plan_performance'] ?? []; Validator::validate( data: $payload, rules: [ 'month' => ['bail', 'required', 'date_format:Y-m'], 'store_category_id' => ['bail', 'required', Rule::exists(Keyword::class, 'key')], 'store_level_id' => ['bail', 'nullable', Rule::exists(Keyword::class, 'key')], 'performance' => ['bail', 'required', 'numeric', 'min:0'], ], attributes: [ 'month' => __('plan.plan_performance.month'), 'store_category_id' => __('plan.plan_performance.store_category_id'), 'store_level_id' => __('plan.plan_performance.store_level_id'), 'performance' => __('plan.plan_performance.performance'), ], ); $planable = PlanPerformance::create($payload); $planable->plan()->create([ 'name' => $data['name'], 'plan_status' => PlanStatus::Pending, ]); break; } return true; } public function update($primaryKey, $data): bool { /** @var \App\Models\Plan|null */ $plan = Plan::findOrFail($primaryKey); if ($plan->isPublished()) { admin_abort('任务计划已发布'); } $planableType = Relation::getMorphedModel($plan->planable_type); $rules = array_merge( ['name' => ['filled', 'max:255']], match ($planableType) { PlanHygiene::class => [ 'plan_hygiene.month' => ['bail', 'required', 'date_format:Y-m'], ], PlanPerformance::class => [ 'plan_performance.month' => ['bail', 'required', 'date_format:Y-m'], 'plan_performance.store_category_id' => ['bail', 'required', Rule::exists(Keyword::class, 'key')], 'plan_performance.store_level_id' => ['bail', 'nullable', Rule::exists(Keyword::class, 'key')], 'plan_performance.performance' => ['bail', 'required', 'numeric', 'min:0'], ], }, ); Validator::validate( data: $data, rules: $rules, attributes: [ 'name' => __('plan.plan.name'), 'plan_performance.month' => __('plan.plan_performance.month'), 'plan_performance.store_category_id' => __('plan.plan_performance.store_category_id'), 'plan_performance.store_level_id' => __('plan.plan_performance.store_level_id'), 'plan_performance.performance' => __('plan.plan_performance.performance'), 'plan_hygiene.month' => __('plan.plan_hygiene.month'), ], ); if (array_key_exists('name', $data)) { $plan->name = $data['name']; } $plan->save(); switch ($planableType) { case PlanPerformance::class: $plan->planable->update($data['plan_performance']); break; case PlanHygiene::class: $plan->planable->update($data['plan_hygiene']); break; } return true; } public function publish($id) { /** @var \App\Models\Plan */ $plan = Plan::findOrFail($id); if ($plan->isPublished()) { admin_abort('任务计划已发布'); } switch (Relation::getMorphedModel($plan->planable_type)) { // 清洁卫生 case PlanHygiene::class: // 任务开始时间 $startAt = Carbon::createFromFormat('Y-m-d H:i:s', "{$plan->planable->month}-01 00:00:00"); // 任务结束时间 $endAt = $startAt->copy()->endOfMonth(); /** @var \Illuminate\Database\Eloquent\Collection */ $stores = Store::findMany($plan->planable->store_ids); foreach ($stores as $store) { $taskable = TaskHygiene::create([ 'month' => $plan->planable->month, 'store_id' => $store->id, 'store_master_id' => $store->master_id, ]); $taskable->task()->create([ 'plan_id' => $plan->id, 'name' => '清洁任务', 'start_at' => $startAt, 'end_at' => $endAt, 'task_status' => TaskStatus::Pending, ]); } break; // 业绩指标 case PlanPerformance::class: // 任务开始时间 $startAt = Carbon::createFromFormat('Y-m-d H:i:s', "{$plan->planable->month}-01 00:00:00"); // 任务结束时间 $endAt = $startAt->copy()->endOfMonth(); // 门店分类IDs $categoryIds = value(function ($id) { $ids = collect(); if ($parent = Keyword::where('key', $id)->first()) { /** @var \Illuminate\Support\Collection */ $ids = Keyword::where('path', 'like', "%-{$parent->id}-%")->pluck('key'); } return $ids->push($id); }, $plan->planable->store_category_id); // 门店业绩统计 $aggregates = Ledger::select(['store_id', DB::raw('SUM(`sales`) as sales')]) ->whereBetween('date', [$startAt->format('Y-m-d'), $endAt->format('Y-m-d')]) ->groupBy('store_id') ->pluck('sales', 'store_id') ->toArray(); /** @var \Illuminate\Database\Eloquent\Collection */ $stores = Store::filter(['level_id' => $plan->planable->store_level_id], StoreFilter::class) ->whereIn('category_id', $categoryIds) ->get(['id', 'master_id']); foreach ($stores as $store) { $taskable = TaskPerformance::create([ 'month' => $plan->planable->month, 'store_id' => $store->id, 'store_master_id' => $store->master_id, 'expected_performance' => $plan->planable->performance, 'actual_performance' => $aggregates[$store->id] ?? 0, ]); $task = new Task([ 'plan_id' => $plan->id, 'name' => '业绩指标', 'start_at' => $startAt, 'end_at' => $endAt, 'task_status' => TaskStatus::Pending, ]); if ($taskable->isSuccess()) { $task->task_status = TaskStatus::Success; $task->completed_at = now(); } $taskable->task()->save($task); } break; default: admin_abort('未知计划类型'); break; } $plan->update([ 'plan_status' => PlanStatus::Published, ]); } public function getDetail($id) { if ($plan = parent::getDetail($id)) { if ($plan->planable instanceof PlanHygiene) { $plan->planable->setAttribute( 'stores', Store::findMany($plan->planable->store_ids) ); } } return $plan; } public function preDelete(array $ids): void { /** @var \Illuminate\Database\Eloquent\Collection */ $plans = Plan::findMany($ids); if ($plans->contains(fn (Plan $plan) => $plan->isPublished())) { admin_abort('不能删除已发布的任务计划'); } $plans->each(fn (Plan $plan) => $plan->planable()->delete()); } public function addRelations($query, string $scene = 'list') { if (in_array($scene, ['edit', 'detail'])) { $query->with([ 'planable' => function (MorphTo $morphTo) { $morphTo->morphWith([ PlanPerformance::class => ['storeCategory', 'storeLevel'], ]); }, ]); } } }