api workflow

main
panliang 2024-04-28 01:30:24 +08:00
parent 6f0887b032
commit e42375b874
18 changed files with 316 additions and 139 deletions

View File

@ -44,6 +44,7 @@ class AgreementController extends AdminController
->placeholder(__('employee.name').'/'.__('employee.phone'))
->columnRatio(3)
->clearable(),
amis()->SelectControl()->name('check_status')->label(__('workflow_log.check_status'))->options(CheckStatus::options())->columnRatio(3)->clearable(),
amisMake()->DateRangeControl()
->name('date_range')
->label(__('agreement.created_at'))

View File

@ -100,17 +100,17 @@ class WorkflowController extends AdminController
amisMake()->TextControl()->name('name')->label(__('workflow.name'))->required(),
amisMake()->TableControl()->name('config')->label(__('workflow.config'))->showIndex()->addable()->removable()->needConfirm(false)->draggable()->columns([
amisMake()->SelectControl()->options(CheckType::options())->name('type')->label(__('workflow.type')),
amisMake()->PickerControl()
->source(admin_url('api/workflow/value-options?type=${type}'))
->multiple(false)
->pickerSchema(amisMake()->ListRenderer()->set('mode', 'list')->listItem(amisMake()->ListItem()->title('${label}')->body('${phone}')))
// ->pickerSchema(amisMake()->CRUD2Table()->mode('list')->listItem(amisMake()->ListItem()->title('${label}')->body('${phone}')))
->name('value')
->label(__('workflow.value')),
// amisMake()->SelectControl()
// amisMake()->PickerControl()
// ->source(admin_url('api/workflow/value-options?type=${type}'))
// ->multiple(false)
// ->pickerSchema(amisMake()->ListRenderer()->set('mode', 'list')->listItem(amisMake()->ListItem()->title('${label}')->body('${phone}')))
// ->pickerSchema(amisMake()->CRUD2Table()->mode('list')->listItem(amisMake()->ListItem()->title('${label}')->body('${phone}')))
// ->name('value')
// ->label(__('workflow.value')),
amisMake()->SelectControl()
->source(admin_url('api/workflow/value-options?type=${type}'))
->name('value')
->label(__('workflow.value')),
]),
]);
}

View File

@ -18,6 +18,9 @@ class AgreementFilter extends ModelFilter
'invitor_name' => 'name',
'invitor_search' => 'search',
],
'workflow' => [
'check_status' => 'check_status'
]
];
public function employeeId($key)

View File

@ -21,6 +21,9 @@ class EmployeePromotionFilter extends ModelFilter
'invitor_name' => 'name',
'invitor_search' => 'search',
],
'workflow' => [
'check_status' => 'check_status'
]
];
public function employeeId($key)

View File

@ -17,6 +17,9 @@ class OvertimeApplyFilter extends ModelFilter
'employee_name' => 'name',
'employee_search' => 'search',
],
'workflow' => [
'check_status' => 'check_status'
]
];
public function employeeId($key)
@ -24,11 +27,6 @@ class OvertimeApplyFilter extends ModelFilter
$this->where('employee_id', $key);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
public function dateRange($key)
{
$dates = explode(',', $key);

View File

@ -208,7 +208,7 @@ class TaskService extends BaseService
/** @var \Illuminate\Database\Eloquent\Collection */
$tasks = Task::findMany($ids);
$tasks->each(fn (Task $task) => $task->taskable()->delete());
$tasks->each(fn (Task $task) => $task->taskable->delete());
}
public function addRelations($query, string $scene = 'list')

View File

@ -112,6 +112,9 @@ class WorkFlowService extends BaseService
'checked_at' => data_get($options, 'checked_at', now()),
]);
// 删除未审核的流程
$check->logs()->where('check_status', CheckStatus::None)->delete();
$check->subject->checkFail();
return true;
@ -185,9 +188,9 @@ class WorkFlowService extends BaseService
]);
$log->update(['check_status' => CheckStatus::Processing]);
// 自动审核通过
if ($this->authCheck($check->employee, $log)) {
return $this->check($check->employee, $log, true, ['remarks' => '自动审核通过']);
}
// if ($this->authCheck($check->employee, $log)) {
// return $this->check($check->employee, $log, true, ['remarks' => '自动审核通过']);
// }
} else {
// 没有审核流程了, 审核完成
return $this->success($check);

View File

@ -76,8 +76,8 @@ MySQL;
$task = Task::with([
'taskable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
TaskHygiene::class => ['workflow', 'store'],
TaskLedger::class => ['store'],
TaskHygiene::class => ['workflow', 'store', 'storeMaster'],
TaskLedger::class => ['store', 'storeMaster'],
]);
},
])->whereHasMorph(

View File

@ -0,0 +1,216 @@
<?php
namespace App\Http\Controllers\Api;
use App\Admin\Services\WorkFlowService;
use App\Enums\CheckStatus;
use App\Exceptions\RuntimeException;
use App\Http\Resources\TaskResource;
use App\Http\Resources\WorkflowLogResource;
use App\Models\Task;
use App\Models\TaskHygiene;
use App\Models\TaskLedger;
use App\Models\WorkflowCheck;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Http\{Request, Response};
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
class WorkflowController extends Controller
{
public function index(Request $request)
{
$request->validate([
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
$model = Relation::getMorphedModel($subjectType);
$resource = $this->mapResource($model);
$user = $request->user();
$include = ['workflow'];
if ($request->input('include')) {
$explodes = explode(',', $request->input('include'));
$include = array_merge($include, $explodes);
}
$query = $model::query()->with($include)
// ->whereHas('workflow', fn($q) => $q->where('check_status', CheckStatus::Processing))
->whereHas('workflow.logs', fn($q) => $q->own($user)->where('check_status', '>', CheckStatus::None->value))
->orderBy('created_at', 'desc');
$list = $query->paginate($request->input('per_page'));
switch ($model) {
case TaskHygiene::class:
$list->loadMissing(['task', 'store']);
$list->through(function (TaskHygiene $item) {
return tap($item->task)->setRelation('taskable', $item->unsetRelation('task'));
});
break;
}
return $resource::collection($list);
}
public function show($id, Request $request)
{
$request->validate([
'subject_type' => 'required',
]);
$user = $request->user();
$subjectType = $request->input('subject_type');
$model = Relation::getMorphedModel($subjectType);
$resource = $this->mapResource($model);
// 当前用户是否可以审核
$checkable = false;
$include = ['workflow'];
if ($request->input('include')) {
$explodes = explode(',', $request->input('include'));
$include = array_merge($include, $explodes);
}
switch ($model) {
case TaskHygiene::class:
$info = Task::with([
'taskable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
TaskHygiene::class => ['workflow', 'store', 'storeMaster'],
TaskLedger::class => ['store', 'storeMaster'],
]);
},
])->findOrFail($id);
$info->taskable->setRelation('task', $info->withoutRelations());
$checkable = $info->taskable->workflow->logs()->own($user)->where('check_status', CheckStatus::Processing)->exists();
break;
default:
$info = $model::query()->with($include)->findOrFail($id);
$checkable = $info->workflow->logs()->own($user)->where('check_status', CheckStatus::Processing)->exists();
break;
}
return ['data' => $resource::make($info), 'checkable' => $checkable];
}
public function logs($id, Request $request)
{
$request->validate([
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
$logs = $check->logs()->with(['checkUser'])->sort()->get();
return WorkflowLogResource::collection($logs);
}
public function cancel($id, Request $request, WorkFlowService $workFlowService)
{
$request->validate([
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
try {
DB::beginTransaction();
if (!$workFlowService->cancel($check)) {
throw new RuntimeException($workFlowService->getError());
}
DB::commit();
return response('', Response::HTTP_OK);
} catch (\Exception $e) {
DB::rollBack();
throw new RuntimeException($e->getMessage());
}
}
public function check($id, Request $request, WorkFlowService $workFlowService)
{
$request->validate([
'subject_type' => 'required',
'status' => ['required'],
'remarks' => [Rule::requiredIf(fn() => !$request->input('status'))]
], [
'remarks.required_if' => '未通过原因必填',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
$user = $this->guard()->user();
try {
DB::beginTransaction();
$log = $check->logs()->where('check_status', CheckStatus::Processing)->first();
if (!$log) {
throw new RuntimeException('审核已经完成');
}
if (!$workFlowService->check($user, $log, !!$request->input('status'), ['remarks' => $request->input('remarks')])) {
throw new RuntimeException($workFlowService->getError());
}
DB::commit();
return response('', Response::HTTP_OK);
} catch (\Exception $e) {
DB::rollBack();
throw new RuntimeException($e->getMessage());
}
}
protected function mapResource(string $model)
{
$class = match ($model) {
TaskHygiene::class => TaskResource::class,
default => 'App\\Http\\Resources\\'.class_basename($model).'Resource',
};
if (! class_exists($class)) {
throw new RuntimeException('未知的 subject_type');
}
return $class;
}
}

View File

@ -10,7 +10,7 @@ use App\Http\Resources\WorkflowLogResource;
use App\Models\Task;
use App\Models\TaskHygiene;
use App\Models\TaskLedger;
use App\Models\WorkflowCheck;
use App\Models\{WorkflowCheck, WorkflowLog};
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
@ -26,100 +26,45 @@ class WorkflowController extends Controller
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
$model = Relation::getMorphedModel($subjectType);
$resource = $this->mapResource($model);
$user = $request->user();
$include = ['workflow'];
$user = $this->guard()->user();
$include = ['check'];
if ($request->input('include')) {
$explodes = explode(',', $request->input('include'));
$include = array_merge($include, $explodes);
}
$list = WorkflowLog::with($include)
->whereHas('check', fn($q) => $q->where('subject_type', $subjectType))
->own($user)
->where('check_status', '>', CheckStatus::None->value)
->orderBy('check_status')
->orderBy('id', 'desc')
->paginate($request->input('per_page'));
$query = $model::query()->with($include)
// ->whereHas('workflow', fn($q) => $q->where('check_status', CheckStatus::Processing))
->whereHas('workflow.logs', fn($q) => $q->own($user)->where('check_status', '>', CheckStatus::None->value))
->orderBy('created_at', 'desc');
$list = $query->paginate($request->input('per_page'));
switch ($model) {
case TaskHygiene::class:
$list->loadMissing(['task', 'store']);
$list->through(function (TaskHygiene $item) {
return tap($item->task)->setRelation('taskable', $item->unsetRelation('task'));
});
break;
}
return $resource::collection($list);
return WorkflowLogResource::collection($list);
}
public function show($id, Request $request)
public function show($id, Request $request, WorkFlowService $service)
{
$request->validate([
'subject_type' => 'required',
]);
$user = $request->user();
$subjectType = $request->input('subject_type');
$model = Relation::getMorphedModel($subjectType);
$resource = $this->mapResource($model);
$include = ['check'];
if ($request->input('include')) {
$explodes = explode(',', $request->input('include'));
$include = array_merge($include, $explodes);
}
$info = WorkflowLog::with($include)->findOrFail($id);
// 当前用户是否可以审核
$checkable = false;
$include = ['workflow'];
if ($request->input('include')) {
$explodes = explode(',', $request->input('include'));
$include = array_merge($include, $explodes);
$user = $this->guard()->user();
if ($user && $info->check_status == CheckStatus::Processing && $service->authCheck($user, $info)) {
$checkable = true;
}
switch ($model) {
case TaskHygiene::class:
$info = Task::with([
'taskable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
TaskHygiene::class => ['workflow', 'store'],
TaskLedger::class => ['store'],
]);
},
])->findOrFail($id);
$info->taskable->setRelation('task', $info->withoutRelations());
$checkable = $info->taskable->workflow->logs()->own($user)->where('check_status', CheckStatus::Processing)->exists();
break;
default:
$info = $model::query()->with($include)->findOrFail($id);
$checkable = $info->workflow->logs()->own($user)->where('check_status', CheckStatus::Processing)->exists();
break;
}
return ['data' => $resource::make($info), 'checkable' => $checkable];
return ['data' => WorkflowLogResource::make($info), 'checkable' => $checkable];
}
public function logs($id, Request $request)
{
$request->validate([
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
$check = WorkflowCheck::findOrFail($id);
$logs = $check->logs()->with(['checkUser'])->sort()->get();
return WorkflowLogResource::collection($logs);
@ -127,23 +72,8 @@ class WorkflowController extends Controller
public function cancel($id, Request $request, WorkFlowService $workFlowService)
{
$request->validate([
'subject_type' => 'required',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
$user = $this->guard()->user();
$check = WorkflowCheck::where('employee_id', $user->id)->findOrFail($id);
try {
DB::beginTransaction();
if (!$workFlowService->cancel($check)) {
@ -161,33 +91,15 @@ class WorkflowController extends Controller
public function check($id, Request $request, WorkFlowService $workFlowService)
{
$request->validate([
'subject_type' => 'required',
'status' => ['required'],
'remarks' => [Rule::requiredIf(fn() => !$request->input('status'))]
], [
'remarks.required_if' => '未通过原因必填',
]);
$subjectType = $request->input('subject_type');
if ($subjectType == (new TaskHygiene)->getMorphClass()) {
$task = Task::findOrFail($id);
$taskable = $task->taskable;
if (!$taskable) {
throw new RuntimeException('任务不存在');
}
$check = $taskable->workflow;
if (!$check) {
throw new RuntimeException('审核记录不存在');
}
} else {
$check = WorkflowCheck::where('subject_type', $subjectType)->where('subject_id', $id)->firstOrFail();
}
$log = WorkflowLog::findOrFail($id);
$user = $this->guard()->user();
try {
DB::beginTransaction();
$log = $check->logs()->where('check_status', CheckStatus::Processing)->first();
if (!$log) {
throw new RuntimeException('审核已经完成');
}
if (!$workFlowService->check($user, $log, !!$request->input('status'), ['remarks' => $request->input('remarks')])) {
throw new RuntimeException($workFlowService->getError());
}

View File

@ -17,6 +17,7 @@ class EmployeeSignRepairResource extends JsonResource
return [
'id' => $this->id,
'date' => $this->date->timestamp,
'date_format' => $this->date->format('Y-m-d H:i'),
'employee_id' => $this->employee_id,
'employee' => EmployeeResource::make($this->whenLoaded('employee')),
@ -28,6 +29,7 @@ class EmployeeSignRepairResource extends JsonResource
'sign_type' => $this->sign_type,
'outside_remarks' => $this->outside_remarks,
'created_at' => $this->created_at->timestamp,
'created_format' => $this->created_at->format('Y-m-d H:i:s'),
'workflow_check' => WorkflowCheckResource::make($this->whenLoaded('workflow')),
];

View File

@ -28,10 +28,13 @@ class HolidayApplyResource extends JsonResource
'workflow_check' => WorkflowCheckResource::make($this->whenLoaded('workflow')),
'start_at' => $this->start_at->timestamp,
'start_format' => $this->start_at->format('Y-m-d H:i'),
'end_at' => $this->end_at->timestamp,
'end_format' => $this->end_at->format('Y-m-d H:i'),
'reason' => $this->reason,
'created_at' => $this->created_at->timestamp,
'created_format' => $this->created_at->format('Y-m-d H:i:s')
];
}
}

View File

@ -25,11 +25,15 @@ class OfficalBusinessResource extends JsonResource
'workflow_check' => WorkflowCheckResource::make($this->whenLoaded('workflow')),
'start_at' => $this->start_at->timestamp,
'start_format' => $this->start_at->format('Y-m-d H:i'),
'end_at' => $this->end_at->timestamp,
'end_format' => $this->end_at->format('Y-m-d H:i'),
'address' => $this->address,
'reason' => $this->reason,
'created_at' => $this->created_at->timestamp,
'created_format' => $this->created_at->format('Y-m-d H:i:s')
];
}
}

View File

@ -25,8 +25,11 @@ class OvertimeApplyResource extends JsonResource
'workflow_check' => WorkflowCheckResource::make($this->whenLoaded('workflow')),
'date' => $this->date->timestamp,
'date_format' => $this->date->format('Y-m-d'),
'start_at' => $this->start_at->timestamp,
'start_format' => $this->start_at->format('H:i'),
'end_at' => $this->end_at->timestamp,
'end_format' => $this->end_at->format('H:i'),
'hours' => $this->hours,
'reason' => $this->reason,

View File

@ -27,6 +27,7 @@ class ReimbursementResource extends JsonResource
'reason' => $this->reason,
'photos' => $this->photos,
'created_at' => $this->created_at?->getTimestamp(),
'created_at_format' => $this->created_at->format('Y-m-d H:i:s'),
'updated_at' => $this->updated_at?->getTimestamp(),
];
}

View File

@ -4,6 +4,7 @@ namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Enums\TaskHygieneStatus;
class TaskHygieneResource extends JsonResource
{
@ -15,13 +16,19 @@ class TaskHygieneResource extends JsonResource
public function toArray(Request $request): array
{
return [
'id' => $this->resource->id,
'month' => $this->resource->month,
'id' => $this->id,
'month' => $this->month,
'store_id' => $this->store_id,
'store' => StoreResource::make($this->whenLoaded('store')),
'description' => $this->resource->description,
'photos' => $this->resource->photos,
'status' => $this->resource->task_status,
'created_at' => $this->resource->created_at->timestamp,
'description' => $this->description,
'photos' => $this->photos,
'status' => $this->task_status,
'status_text' => $this->task_status->text(),
'created_at' => $this->created_at->timestamp,
'store_master_id' => $this->store_master_id,
'store_master' => EmployeeResource::make($this->whenLoaded('storeMaster')),
'task_id' => $this->task_id,
'task' => TaskResource::make($this->whenLoaded('task')),
];
}
}

View File

@ -20,6 +20,8 @@ class TaskLedgerResource extends JsonResource
'store' => StoreResource::make($this->whenLoaded('store')),
'status' => $this->resource->task_status,
'created_at' => $this->resource->created_at->timestamp,
'store_master_id' => $this->store_master_id,
'store_master' => EmployeeResource::make($this->whenLoaded('storeMaster')),
];
}
}

View File

@ -4,6 +4,7 @@ namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Database\Eloquent\Relations\Relation;
class WorkflowCheckResource extends JsonResource
{
@ -14,12 +15,30 @@ class WorkflowCheckResource extends JsonResource
*/
public function toArray(Request $request): array
{
$resource = $this->mapResource($this->subject_type);
return [
'check_status' => $this->check_status,
'check_status_text' => $this->check_status?->text(),
'checked_at' => $this->checked_at?->getTimestamp(),
'check_remarks' => (string) $this->check_remarks,
'logs' => WorkflowLogResource::collection($this->whenLoaded('logs')),
'subject_id' => $this->subject_id,
'subject_type' => $this->subject_type,
'subject' => $resource::make($this->whenLoaded('subject')),
];
}
protected function mapResource(string $type)
{
$model = Relation::getMorphedModel($type);
$class = match ($model) {
default => 'App\\Http\\Resources\\'.class_basename($model).'Resource',
};
if (! class_exists($class)) {
throw new RuntimeException('未知的 subject_type');
}
return $class;
}
}