diff --git a/app/Admin/Controllers/Plan/PlanController.php b/app/Admin/Controllers/Plan/PlanController.php
index f923508..02f10a4 100644
--- a/app/Admin/Controllers/Plan/PlanController.php
+++ b/app/Admin/Controllers/Plan/PlanController.php
@@ -5,6 +5,8 @@ namespace App\Admin\Controllers\Plan;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\Plan\PlanService;
use App\Enums\PlanStatus;
+use App\Enums\TaskLedgerStatus;
+use App\Enums\TaskPerformanceStatus;
use App\Enums\TaskStatus;
use App\Models\PlanHygiene;
use App\Models\PlanLedger;
@@ -198,12 +200,10 @@ class PlanController extends AdminController
amis()->CRUDTable()
->api(admin_url('api/tasks?plan_id=${id}'))
->columns([
- // amis()->TableColumn('id', __('plan.task.id')),
- // amis()->TableColumn('name', __('plan.task.name')),
amis()->TableColumn('taskable.date', __('plan.task_ledger.date')),
amis()->TableColumn('taskable.store.title', __('plan.task_ledger.store')),
amis()->TableColumn('taskable.store_master.name', __('plan.task_ledger.store_master')),
- amis()->TableColumn('task_status', __('plan.task.status'))->type('mapping')->map(TaskStatus::labelMap()),
+ amis()->TableColumn('taskable.task_status', __('plan.task_ledger.status'))->type('mapping')->map(TaskLedgerStatus::labelMap()),
amis()->TableColumn('completed_at', __('plan.task.completed_at')),
amis()->TableColumn('created_at', __('plan.task.created_at')),
])
@@ -219,14 +219,12 @@ class PlanController extends AdminController
amis('reload')->align('right'),
])
->columns([
- // amis()->TableColumn('id', __('plan.task.id')),
- // amis()->TableColumn('name', __('plan.task.name')),
amis()->TableColumn('taskable.month', __('plan.task_performance.month')),
amis()->TableColumn('taskable.store.title', __('plan.task_performance.store')),
amis()->TableColumn('taskable.store_master.name', __('plan.task_performance.store_master')),
amis()->TableColumn('taskable.actual_performance', __('plan.task_performance.actual_performance')),
amis()->TableColumn('taskable.expected_performance', __('plan.task_performance.expected_performance')),
- amis()->TableColumn('task_status', __('plan.task.status'))->type('mapping')->map(TaskStatus::labelMap()),
+ amis()->TableColumn('taskable.task_status', __('plan.task_performance.status'))->type('mapping')->map(TaskPerformanceStatus::labelMap()),
amis()->TableColumn('completed_at', __('plan.task.completed_at')),
amis()->TableColumn('created_at', __('plan.task.created_at')),
$this->rowActions([
diff --git a/app/Admin/Controllers/Plan/TaskController.php b/app/Admin/Controllers/Plan/TaskController.php
index 17d74fe..d089015 100644
--- a/app/Admin/Controllers/Plan/TaskController.php
+++ b/app/Admin/Controllers/Plan/TaskController.php
@@ -34,6 +34,10 @@ class TaskController extends AdminController
->latest('id')
->get();
- return $this->response()->success($tasks);
+ return $this->response()->success(
+ $tasks->map(function (Task $task) {
+ return tap($task, fn (Task $task) => $task->taskable?->setRelation('task', $task->withoutRelations()));
+ })
+ );
}
}
diff --git a/app/Admin/Services/Plan/PlanService.php b/app/Admin/Services/Plan/PlanService.php
index a7dcfcb..d95bb5e 100644
--- a/app/Admin/Services/Plan/PlanService.php
+++ b/app/Admin/Services/Plan/PlanService.php
@@ -16,6 +16,7 @@ use App\Models\TaskHygiene;
use App\Models\TaskPerformance;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
+use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
@@ -32,56 +33,81 @@ class PlanService extends BaseService
public function store($data): bool
{
- $planableTypePerformance = (new PlanPerformance())->getMorphClass();
- $planableTypeHygiene = (new PlanHygiene())->getMorphClass();
-
- $rules = array_merge(
- [
- 'name' => ['bail', 'required', 'max:255'],
- 'planable_type' => ['bail', 'required', Rule::in([$planableTypePerformance, $planableTypeHygiene])],
- ],
- match ($data['planable_type'] ?? '') {
- $planableTypePerformance => [
- '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'],
- ],
- $planableTypeHygiene => [
- 'plan_hygiene.month' => ['bail', 'required', 'date_format:Y-m'],
- ],
- },
- );
-
Validator::validate(
data: $data,
- rules: $rules,
+ rules: [
+ 'name' => ['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'),
- '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'),
],
);
- switch ($data['planable_type']) {
- case $planableTypePerformance:
- $planable = PlanPerformance::create($data['plan_performance']);
+ $planableType = Relation::getMorphedModel($data['planable_type']);
+
+ switch ($planableType) {
+ // 清洁卫生
+ 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 = PlanPerformance::create($payload);
+
+ $planable->plan()->create([
+ 'name' => $data['name'],
+ 'plan_status' => PlanStatus::Pending,
+ ]);
+
break;
- case $planableTypeHygiene:
- $planable = PlanHygiene::create($data['plan_hygiene']);
+ // 业绩指标
+ 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;
}
- $planable->plan()->create([
- 'name' => $data['name'],
- 'plan_status' => PlanStatus::Pending,
- ]);
-
return true;
}
@@ -94,20 +120,21 @@ class PlanService extends BaseService
admin_abort('任务计划已发布');
}
+ $planableType = Relation::getMorphedModel($plan->planable_type);
+
$rules = array_merge(
- [
- 'name' => ['filled', 'max:255'],
- ],
- match (get_class($plan->planable)) {
+ ['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'],
],
- PlanHygiene::class => [
- 'plan_hygiene.month' => ['bail', 'required', 'date_format:Y-m'],
- ],
},
);
@@ -129,7 +156,7 @@ class PlanService extends BaseService
}
$plan->save();
- switch (get_class($plan->planable)) {
+ switch ($planableType) {
case PlanPerformance::class:
$plan->planable->update($data['plan_performance']);
break;
@@ -151,22 +178,20 @@ class PlanService extends BaseService
admin_abort('任务计划已发布');
}
- switch (get_class($planable = $plan->planable)) {
+ switch (Relation::getMorphedModel($plan->planable_type)) {
// 清洁卫生
case PlanHygiene::class:
- // 任务计划月份
- $month = Carbon::createFromFormat('Y-m', $planable->month);
// 任务开始时间
- $startAt = $month->copy()->startOfMonth();
+ $startAt = Carbon::createFromFormat('Y-m-d H:i:s', "{$plan->planable->month}-01 00:00:00");
// 任务结束时间
- $endAt = $month->copy()->endOfMonth();
+ $endAt = $startAt->copy()->endOfMonth();
/** @var \Illuminate\Database\Eloquent\Collection */
- $stores = Store::findMany($planable->store_ids);
+ $stores = Store::findMany($plan->planable->store_ids);
foreach ($stores as $store) {
$taskable = TaskHygiene::create([
- 'month' => $planable->month,
+ 'month' => $plan->planable->month,
'store_id' => $store->id,
'store_master_id' => $store->master_id,
]);
@@ -182,16 +207,14 @@ class PlanService extends BaseService
// 业绩指标
case PlanPerformance::class:
- // 任务计划月份
- $month = Carbon::createFromFormat('Y-m', $planable->month);
// 任务开始时间
- $startAt = $month->copy()->startOfMonth();
+ $startAt = Carbon::createFromFormat('Y-m-d H:i:s', "{$plan->planable->month}-01 00:00:00");
// 任务结束时间
- $endAt = $month->copy()->endOfMonth();
+ $endAt = $startAt->copy()->endOfMonth();
$input = [
- 'category_id' => $planable->store_category_id,
- 'level_id' => $planable->store_level_id,
+ 'category_id' => $plan->planable->store_category_id,
+ 'level_id' => $plan->planable->store_level_id,
];
/** @var \Illuminate\Database\Eloquent\Collection */
@@ -199,10 +222,10 @@ class PlanService extends BaseService
foreach ($stores as $store) {
$taskable = TaskPerformance::create([
- 'month' => $planable->month,
+ 'month' => $plan->planable->month,
'store_id' => $store->id,
'store_master_id' => $store->master_id,
- 'expected_performance' => $planable->performance,
+ 'expected_performance' => $plan->planable->performance,
'actual_performance' => 0,
]);
$taskable->task()->create([
diff --git a/app/Enums/TaskLedgerStatus.php b/app/Enums/TaskLedgerStatus.php
new file mode 100644
index 0000000..3bc9abe
--- /dev/null
+++ b/app/Enums/TaskLedgerStatus.php
@@ -0,0 +1,36 @@
+value];
+ }
+
+ public static function options(): array
+ {
+ return [
+ self::None->value => '未开始',
+ self::Pending->value => '待完成',
+ self::Success->value => '已完成',
+ self::Failed->value => '未完成',
+ ];
+ }
+
+ public static function labelMap(): array
+ {
+ return [
+ self::None->value => ''.self::None->text().'',
+ self::Pending->value => ''.self::Pending->text().'',
+ self::Success->value => ''.self::Success->text().'',
+ self::Failed->value => ''.self::Failed->text().'',
+ ];
+ }
+}
diff --git a/app/Enums/TaskPerformanceStatus.php b/app/Enums/TaskPerformanceStatus.php
index 232ad60..ca6b218 100644
--- a/app/Enums/TaskPerformanceStatus.php
+++ b/app/Enums/TaskPerformanceStatus.php
@@ -8,4 +8,29 @@ enum TaskPerformanceStatus: int
case Pending = 2; // 待完成
case Success = 3; // 已完成
case Failed = 4; // 未完成
+
+ public function text(): string
+ {
+ return self::options()[$this->value];
+ }
+
+ public static function options(): array
+ {
+ return [
+ self::None->value => '未开始',
+ self::Pending->value => '待完成',
+ self::Success->value => '已完成',
+ self::Failed->value => '未完成',
+ ];
+ }
+
+ public static function labelMap(): array
+ {
+ return [
+ self::None->value => ''.self::None->text().'',
+ self::Pending->value => ''.self::Pending->text().'',
+ self::Success->value => ''.self::Success->text().'',
+ self::Failed->value => ''.self::Failed->text().'',
+ ];
+ }
}
diff --git a/app/Http/Controllers/Api/Account/TaskPerformanceController.php b/app/Http/Controllers/Api/Account/TaskPerformanceController.php
index baa38b8..e114c69 100644
--- a/app/Http/Controllers/Api/Account/TaskPerformanceController.php
+++ b/app/Http/Controllers/Api/Account/TaskPerformanceController.php
@@ -3,10 +3,9 @@
namespace App\Http\Controllers\Api\Account;
use App\Http\Controllers\Api\Controller;
+use App\Http\Resources\TaskPerformanceResource;
use App\Models\TaskPerformance;
use Illuminate\Http\Request;
-use Illuminate\Support\Carbon;
-use Illuminate\Support\Collection;
class TaskPerformanceController extends Controller
{
@@ -16,7 +15,8 @@ class TaskPerformanceController extends Controller
$user = $request->user();
/** @var \Illuminate\Database\Eloquent\Collection */
- $taskPerformances = TaskPerformance::where('store_id', $user->store_id)
+ $taskPerformances = TaskPerformance::with(['task'])
+ ->where('store_id', $user->store_id)
->when($request->input('filter'), function ($query, $filter) {
$now = now();
switch ($filter) {
@@ -30,15 +30,7 @@ class TaskPerformanceController extends Controller
})
->get();
- return $taskPerformances->map(function (TaskPerformance $taskable) {
- return [
- 'id' => $taskable->id,
- 'month' => $taskable->month,
- 'actual_performance' => trim_zeros($taskable->actual_performance),
- 'expected_performance' => trim_zeros($taskable->expected_performance),
- 'status' => $taskable->task_status,
- ];
- });
+ return TaskPerformanceResource::collection($taskPerformances);
// return $taskPerformances->groupBy(fn (TaskPerformance $taskable) => Carbon::createFromFormat('Y-m', $taskable->month)->year)
// ->map(function (Collection $collection) {
diff --git a/app/Http/Controllers/Api/TaskController.php b/app/Http/Controllers/Api/TaskController.php
new file mode 100644
index 0000000..46a20e6
--- /dev/null
+++ b/app/Http/Controllers/Api/TaskController.php
@@ -0,0 +1,52 @@
+user();
+
+ $orderBy = <<<'MySQL'
+ CASE
+ WHEN task_status = 1 THEN 100
+ ELSE 0
+ END
+MySQL;
+
+ /** @var \Illuminate\Database\Eloquent\Collection */
+ $tasks = Task::with(['taskable'])
+ ->whereHasMorph(
+ 'taskable',
+ [TaskHygiene::class, TaskLedger::class],
+ function (Builder $query, string $type) use ($user) {
+ switch ($type) {
+ case TaskLedger::class:
+ case TaskHygiene::class:
+ if ($user->isStoreMaster()) {
+ $query->where('store_id', $user->store_id);
+ } else {
+ $query->whereRaw('1!=1');
+ }
+ break;
+ }
+ }
+ )
+ ->orderBy(DB::raw($orderBy), 'DESC')
+ ->orderBy('start_at', 'ASC')
+ ->orderBy('end_at', 'ASC')
+ ->simplePaginate($request->query('per_page', 20));
+
+ return TaskResource::collection($tasks);
+ }
+}
diff --git a/app/Http/Resources/TaskHygieneResource.php b/app/Http/Resources/TaskHygieneResource.php
new file mode 100644
index 0000000..dd69d58
--- /dev/null
+++ b/app/Http/Resources/TaskHygieneResource.php
@@ -0,0 +1,26 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'id' => $this->resource->id,
+ 'month' => $this->resource->month,
+ 'store' => StoreResource::make($this->whenLoaded('store')),
+ 'description' => $this->resource->description,
+ 'photos' => $this->resource->photos,
+ 'created_at' => $this->resource->created_at->timestamp,
+ ];
+ }
+}
diff --git a/app/Http/Resources/TaskLedgerResource.php b/app/Http/Resources/TaskLedgerResource.php
new file mode 100644
index 0000000..2feb9dc
--- /dev/null
+++ b/app/Http/Resources/TaskLedgerResource.php
@@ -0,0 +1,25 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'id' => $this->resource->id,
+ 'date' => $this->resource->date,
+ 'store' => StoreResource::make($this->whenLoaded('store')),
+ 'status' => $this->resource->task_status,
+ 'created_at' => $this->resource->created_at->timestamp,
+ ];
+ }
+}
diff --git a/app/Http/Resources/TaskPerformanceResource.php b/app/Http/Resources/TaskPerformanceResource.php
new file mode 100644
index 0000000..2808fed
--- /dev/null
+++ b/app/Http/Resources/TaskPerformanceResource.php
@@ -0,0 +1,27 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'id' => $this->resource->id,
+ 'month' => $this->resource->month,
+ 'store' => StoreResource::make($this->whenLoaded('store')),
+ 'actual_performance' => trim_zeros($this->resource->actual_performance),
+ 'expected_performance' => trim_zeros($this->resource->expected_performance),
+ 'status' => $this->resource->task_status,
+ 'created_at' => $this->resource->created_at->timestamp,
+ ];
+ }
+}
diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php
new file mode 100644
index 0000000..8e86343
--- /dev/null
+++ b/app/Http/Resources/TaskResource.php
@@ -0,0 +1,47 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'id' => $this->resource->id,
+ 'name' => $this->resource->name,
+ 'taskable_type' => $this->resource->taskable_type,
+ 'taskable_id' => $this->resource->taskable_id,
+ 'taskable' => $this->when($this->resource->relationLoaded('taskable'), function () {
+ $taskable = $this->resource->taskable;
+
+ switch (Relation::getMorphedModel($this->resource->taskable_type)) {
+ case TaskLedger::class:
+ return TaskLedgerResource::make($taskable);
+
+ case TaskHygiene::class:
+ return TaskHygieneResource::make($taskable);
+
+ case TaskPerformance::class:
+ return TaskPerformanceResource::make($taskable);
+ }
+
+ return $taskable;
+ }),
+ 'start_at' => (int) $this->resource->start_at?->timestamp,
+ 'end_at' => (int) $this->resource->end_at?->timestamp,
+ 'created_at' => $this->resource->created_at->timestamp,
+ ];
+ }
+}
diff --git a/app/Models/TaskLedger.php b/app/Models/TaskLedger.php
index 2be71f8..4fcc565 100644
--- a/app/Models/TaskLedger.php
+++ b/app/Models/TaskLedger.php
@@ -2,7 +2,9 @@
namespace App\Models;
+use App\Enums\TaskLedgerStatus;
use App\Traits\HasDateTimeFormatter;
+use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -12,6 +14,10 @@ class TaskLedger extends Model
{
use HasFactory, HasDateTimeFormatter;
+ protected $appends = [
+ 'task_status',
+ ];
+
protected $fillable = [
'date',
'store_id',
@@ -32,4 +38,30 @@ class TaskLedger extends Model
{
return $this->belongsTo(Employee::class, 'store_master_id');
}
+
+ /**
+ * 任务状态
+ */
+ protected function taskStatus(): Attribute
+ {
+ return Attribute::make(
+ get: function (mixed $value, array $attributes) {
+ /** @var \App\Models\Task */
+ $task = $this->task;
+
+ if ($task->isSuccess()) {
+ return TaskLedgerStatus::Success;
+ }
+
+ $datetime = now();
+
+ if ($datetime->lt($task->start_at)) {
+ return TaskLedgerStatus::None;
+ } elseif ($datetime->gte($task->end_at)) {
+ return TaskLedgerStatus::Failed;
+ }
+ return TaskLedgerStatus::Pending;
+ },
+ );
+ }
}
diff --git a/app/Models/TaskPerformance.php b/app/Models/TaskPerformance.php
index 3a300ce..18bca7c 100644
--- a/app/Models/TaskPerformance.php
+++ b/app/Models/TaskPerformance.php
@@ -9,12 +9,15 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphOne;
-use Illuminate\Support\Carbon;
class TaskPerformance extends Model
{
use HasFactory, HasDateTimeFormatter;
+ protected $appends = [
+ 'task_status',
+ ];
+
protected $fillable = [
'month',
'store_id',
diff --git a/lang/zh_CN/plan.php b/lang/zh_CN/plan.php
index fc5eee2..8fc94ab 100644
--- a/lang/zh_CN/plan.php
+++ b/lang/zh_CN/plan.php
@@ -39,6 +39,7 @@ return [
'date' => '日期',
'store' => '门店',
'store_master' => '店长',
+ 'status' => '状态',
],
'task_performance' => [
@@ -47,11 +48,13 @@ return [
'store_master' => '店长',
'actual_performance' => '实际业绩',
'expected_performance' => '目标业绩',
+ 'status' => '状态',
],
'task_hygiene' => [
'month' => '月份',
'store' => '门店',
'store_master' => '店长',
+ 'status' => '状态',
],
];
diff --git a/routes/api.php b/routes/api.php
index 5ce4df4..938c6cf 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -11,6 +11,7 @@ use App\Http\Controllers\Api\KeywordController;
use App\Http\Controllers\Api\LedgerController;
use App\Http\Controllers\Api\ReimbursementController;
use App\Http\Controllers\Api\StatisticsController;
+use App\Http\Controllers\Api\TaskController;
use Illuminate\Support\Facades\Route;
Route::post('/auth/login', [AccessTokenController::class, 'store']);
@@ -54,6 +55,9 @@ Route::group([
// 数据上报
Route::apiResource('/ledgers', LedgerController::class)->only(['store', 'show']);
+ // 任务列表
+ Route::get('tasks', [TaskController::class, 'index']);
+
// 举报投诉
Route::post('complaints', [ComplaintController::class, 'store']);
// 意见箱