admin 重写审核流程

main
panliang 2024-04-03 18:54:01 +08:00
parent dcdaa909c7
commit d591be918b
31 changed files with 289 additions and 295 deletions

View File

@ -61,16 +61,16 @@ class HolidayController extends AdminController
amisMake()->TableColumn()->name('reason')->label(__('holiday_apply.reason')),
amisMake()->TableColumn()->name('start_at')->label(__('holiday_apply.start_at')),
amisMake()->TableColumn()->name('end_at')->label(__('holiday_apply.end_at')),
amisMake()->TableColumn()->name('check_status')->label(__('holiday_apply.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('workflow.check_status')->label(__('holiday_apply.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('created_at')->label(__('holiday_apply.created_at')),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.holiday.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.holiday.update'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->rowDeleteButton()
->visible(Admin::user()->can('admin.hr.holiday.delete'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->applyAction(),
$this->cancelAction(),
]),
@ -108,7 +108,6 @@ class HolidayController extends AdminController
public function detail(): Form
{
$subjectType = $this->getMorphAlias();
$detail = amisMake()->Property()->items([
['label' => __('holiday_apply.store_id'), 'content' => '${store.title}'],
['label' => __('holiday_apply.employee_id'), 'content' => '${employee.name}'],
@ -116,19 +115,14 @@ class HolidayController extends AdminController
['label' => __('holiday_apply.date'), 'content' => '${start_at} ${end_at}'],
['label' => __('holiday_apply.reason'), 'content' => '${reason}'],
['label' => __('holiday_apply.created_at'), 'content' => '${created_at}'],
['label' => __('holiday_apply.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('holiday_apply.checked_at'), 'content' => '${checked_at}'],
['label' => __('holiday_apply.check_remarks'), 'content' => '${check_remarks}'],
['label' => __('workflow_log.check_status'), 'content' => amisMake()->Mapping()->name('workflow.check_status')->map(CheckStatus::options())],
['label' => __('workflow_log.checked_at'), 'content' => '${workflow.checked_at}'],
['label' => __('workflow_log.remarks'), 'content' => '${workflow.check_remarks}'],
]);
$table = amisMake()->Service()
->id('holiday-checklog-table')
->initFetch(false)
->api(
amisMake()->BaseApi()->method('get')->url(admin_url('api/workflow/logs'))->data([
'subject_type' => $subjectType,
'subject_id' => '${id}',
])
)
->api(admin_url('api/workflow/logs') . '?id=${workflow.id}')
->body(
amisMake()->Table()->columnsTogglable(false)->itemActions([
$this->succesAction()->reload('holiday-detail'),
@ -150,9 +144,4 @@ class HolidayController extends AdminController
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new HolidayApply)->getMorphClass();
}
}

View File

@ -55,16 +55,16 @@ class OfficalBusinessController extends AdminController
amisMake()->TableColumn()->name('end_at')->label(__('offical_business.date')),
amisMake()->TableColumn()->name('address')->label(__('offical_business.address')),
amisMake()->TableColumn()->name('reason')->label(__('offical_business.reason')),
amisMake()->TableColumn()->name('check_status')->label(__('offical_business.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('workflow.check_status')->label(__('workflow_log.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('created_at')->label(__('offical_business.created_at')),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.business.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.business.update'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->rowDeleteButton()
->visible(Admin::user()->can('admin.hr.business.delete'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->applyAction(),
$this->cancelAction(),
]),
@ -95,7 +95,6 @@ class OfficalBusinessController extends AdminController
public function detail(): Form
{
$subjectType = $this->getMorphAlias();
$detail = amisMake()->Property()->items([
['label' => __('offical_business.store_id'), 'content' => '${store.title}'],
['label' => __('offical_business.employee_id'), 'content' => '${employee.name}'],
@ -103,19 +102,14 @@ class OfficalBusinessController extends AdminController
['label' => __('offical_business.address'), 'content' => '${address}'],
['label' => __('offical_business.reason'), 'content' => '${reason}'],
['label' => __('offical_business.created_at'), 'content' => '${created_at}'],
['label' => __('offical_business.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('offical_business.checked_at'), 'content' => '${checked_at}'],
['label' => __('offical_business.check_remarks'), 'content' => '${check_remarks}'],
['label' => __('workflow_log.check_status'), 'content' => amisMake()->Mapping()->name('workflow.check_status')->map(CheckStatus::options())],
['label' => __('workflow_log.checked_at'), 'content' => '${workflow.checked_at}'],
['label' => __('workflow_log.remarks'), 'content' => '${workflow.check_remarks}'],
]);
$table = amisMake()->Service()
->id('offical-business-checklog-table')
->initFetch(false)
->api(
amisMake()->BaseApi()->method('get')->url(admin_url('api/workflow/logs'))->data([
'subject_type' => $subjectType,
'subject_id' => '${id}',
])
)
->api(admin_url('api/workflow/logs') . '?id=${workflow.id}')
->body(
amisMake()->Table()->columnsTogglable(false)->itemActions([
$this->succesAction()->reload('offical-business-detail'),
@ -137,9 +131,4 @@ class OfficalBusinessController extends AdminController
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new OfficalBusiness)->getMorphClass();
}
}

View File

@ -56,16 +56,16 @@ class OvertimeController extends AdminController
amisMake()->TableColumn()->name('employee.name')->label(__('overtime_apply.employee_id')),
amisMake()->TableColumn()->name('date')->label(__('overtime_apply.date')),
amisMake()->TableColumn()->name('hours')->label(__('overtime_apply.hours')),
amisMake()->TableColumn()->name('check_status')->label(__('overtime_apply.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('workflow.check_status')->label(__('workflow_log.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('created_at')->label(__('overtime_apply.created_at')),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.overtime.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.overtime.update'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->rowDeleteButton()
->visible(Admin::user()->can('admin.hr.overtime.delete'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->applyAction(),
$this->cancelAction(),
]),
@ -95,7 +95,6 @@ class OvertimeController extends AdminController
public function detail(): Form
{
$subjectType = $this->getMorphAlias();
$detail = amisMake()->Property()->items([
['label' => __('overtime_apply.store_id'), 'content' => '${store.title}'],
['label' => __('overtime_apply.employee_id'), 'content' => '${employee.name}'],
@ -105,19 +104,14 @@ class OvertimeController extends AdminController
['label' => __('overtime_apply.hours'), 'content' => '${hours}'],
['label' => __('overtime_apply.created_at'), 'content' => '${created_at}'],
['label' => __('overtime_apply.reason'), 'content' => '${reason}', 'span' => 2],
['label' => __('overtime_apply.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('overtime_apply.checked_at'), 'content' => '${checked_at}'],
['label' => __('overtime_apply.check_remarks'), 'content' => '${check_remarks}'],
['label' => __('workflow_log.check_status'), 'content' => amisMake()->Mapping()->name('workflow.check_status')->map(CheckStatus::options())],
['label' => __('workflow_log.checked_at'), 'content' => '${workflow.checked_at}'],
['label' => __('workflow_log.remarks'), 'content' => '${workflow.check_remarks}'],
]);
$table = amisMake()->Service()
->id('overtime-checklog-table')
->initFetch(false)
->api(
amisMake()->BaseApi()->method('get')->url(admin_url('api/workflow/logs'))->data([
'subject_type' => $subjectType,
'subject_id' => '${id}',
])
)
->api(admin_url('api/workflow/logs') . '?id=${workflow.id}')
->body(
amisMake()->Table()->columnsTogglable(false)->itemActions([
$this->succesAction()->reload('overtime-detail'),
@ -139,9 +133,4 @@ class OvertimeController extends AdminController
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new OvertimeApply)->getMorphClass();
}
}

View File

@ -22,6 +22,7 @@ class SignController extends AdminController
->tableLayout('fixed')
->headerToolbar([
...$this->baseHeaderToolBar(),
$this->exportAction(),
])
->bulkActions([])
->filter($this->baseFilter()->body([
@ -38,7 +39,7 @@ class SignController extends AdminController
->columns([
amisMake()->TableColumn()->name('date')->label(__('employee_sign.date')),
amisMake()->TableColumn()->name('store.title')->label(__('employee_sign.store_id')),
amisMake()->TableColumn()->name('employee.name')->label(__('employee.name')),
amisMake()->TableColumn()->name('employee.name')->label(__('employee_sign.employee_id')),
amisMake()->TableColumn()->name('sign_type')->label(__('employee_sign.sign_type'))
->set('type', 'status')
->source(SignType::source()),
@ -93,4 +94,20 @@ class SignController extends AdminController
]
])->body([$detail, amisMake()->Divider()->title(__('employee_sign.log')), $logs]);
}
protected function exportMap($row)
{
$type = SignType::options();
$status = SignStatus::options();
return [
__('employee_sign.date') => data_get($row, 'date'),
__('employee_sign.store_id') => data_get($row, 'store.title'),
__('employee_sign.employee_id') => data_get($row, 'employee.name'),
__('employee_sign.sign_type') => data_get($type, data_get($row, 'sign_type')),
__('employee_sign.first_time') => data_get($row, 'first_time'),
__('employee_sign.last_time') => data_get($row, 'last_time'),
__('employee_sign.sign_status') => data_get($status, data_get($row, 'sign_status')),
__('employee_sign.remarks') => data_get($row, 'remarks'),
];
}
}

View File

@ -22,7 +22,6 @@ class SignRepairController extends AdminController
public function list(): Page
{
$subjectType = (new EmployeeSignRepair)->getMorphClass();
$crud = $this->baseCRUD()
->tableLayout('fixed')
->headerToolbar([
@ -46,12 +45,10 @@ class SignRepairController extends AdminController
amisMake()->DateRangeControl()->name('date_range')->label(__('employee_sign_repair.date'))
->columnRatio(3)
->clearable(),
]),
amis()->GroupControl()->mode('horizontal')->body([
amisMake()->SelectControl()->name('check_status')->label(__('employee_sign_repair.check_status'))
->options(CheckStatus::options())
->columnRatio(3)
->clearable()
amisMake()->SelectControl()->name('check_status')->label(__('employee_sign_repair.check_status'))
->options(CheckStatus::options())
->columnRatio(3)
->clearable()
]),
]))
->columns([
@ -62,17 +59,17 @@ class SignRepairController extends AdminController
amisMake()->Column()->name('repair_type')->label(__('employee_sign_repair.repair_type'))
->set('type', 'mapping')
->map(SignTime::options()),
amisMake()->Column()->name('check_status')->label(__('employee_sign_repair.check_status'))
amisMake()->Column()->name('workflow.check_status')->label(__('employee_sign_repair.check_status'))
->set('type', 'mapping')
->map(CheckStatus::options()),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.repairs.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.repairs.update'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->rowDeleteButton()
->visible(Admin::user()->can('admin.hr.repairs.delete'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}'),
$this->applyAction(),
$this->cancelAction(),
])
@ -105,7 +102,6 @@ class SignRepairController extends AdminController
public function detail(): Form
{
$subjectType = (new EmployeeSignRepair)->getMorphClass();
$detail = amisMake()->Property()->items([
['label' => __('employee_sign_repair.store_id'), 'content' => '${store.title}'],
['label' => __('employee_sign_repair.employee_id'), 'content' => '${employee.name}'],
@ -113,19 +109,14 @@ class SignRepairController extends AdminController
['label' => __('employee_sign_repair.repair_type'), 'content' => amisMake()->Mapping()->name('repair_type')->map(SignTime::options())],
['label' => __('employee_sign_repair.reason'), 'content' => '${reason}'],
['label' => __('employee_sign_repair.created_at'), 'content' => '${created_at}'],
['label' => __('employee_sign_repair.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('employee_sign_repair.checked_at'), 'content' => '${checked_at}'],
['label' => __('employee_sign_repair.check_remarks'), 'content' => '${check_remarks}'],
['label' => __('workflow_log.check_status'), 'content' => amisMake()->Mapping()->name('workflow.check_status')->map(CheckStatus::options())],
['label' => __('workflow_log.checked_at'), 'content' => '${workflow.checked_at}'],
['label' => __('workflow_log.check_remarks'), 'content' => '${workflow.check_remarks}'],
]);
$table = amisMake()->Service()
->id('sign-repair-checklog-table')
->initFetch(false)
->api(
amisMake()->BaseApi()->method('get')->url(admin_url('api/workflow/logs'))->data([
'subject_type' => $subjectType,
'subject_id' => '${id}',
])
)
->api(admin_url('api/workflow/logs') . '?id=${workflow.id}')
->body(
amisMake()->Table()->columnsTogglable(false)->itemActions([
$this->succesAction()->reload('sign-repair-detail'),
@ -147,9 +138,4 @@ class SignRepairController extends AdminController
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new EmployeeSignRepair)->getMorphClass();
}
}

View File

@ -5,7 +5,7 @@ namespace App\Admin\Controllers\System;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\WorkFlowService;
use App\Enums\CheckType;
use App\Models\{Employee, WorkflowLog};
use App\Models\{Employee, WorkflowLog, WorkflowCheck};
use App\Models\Keyword;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
@ -92,16 +92,8 @@ class WorkflowController extends AdminController
public function apply(Request $request)
{
$type = $request->input('subject_type');
$id = $request->input('subject_id');
$subject = Relation::getMorphedModel($type);
$model = (new $subject)->findOrFail($id);
$user = $request->input('user');
if ($request->filled('user')) {
$user = Employee::findOrFail($request->input('user'));
} else {
$user = $model->employee;
}
$model = WorkflowCheck::findOrFail($request->input('id'));
$user = Employee::findOrFail($request->input('user'));
try {
DB::beginTransaction();
@ -118,10 +110,7 @@ class WorkflowController extends AdminController
public function cancel(Request $request)
{
$type = $request->input('subject_type');
$id = $request->input('subject_id');
$subject = Relation::getMorphedModel($type);
$model = (new $subject)->findOrFail($id);
$model = WorkflowCheck::findOrFail($request->input('id'));
try {
DB::beginTransaction();
@ -183,9 +172,7 @@ class WorkflowController extends AdminController
public function logs(Request $request)
{
$type = $request->input('subject_type');
$id = $request->input('subject_id');
$list = WorkflowLog::with(['checkUser'])->where('subject_type', $type)->where('subject_id', $id)->sort()->get();
$list = WorkflowLog::with(['checkUser'])->where('check_id', $request->input('id'))->sort()->get();
return $this->response()->success($list);
}

View File

@ -9,6 +9,19 @@ class EmployeeSignRepairFilter extends ModelFilter
{
protected $drop_id = false;
public $relations = [
'workflow' => [
'check_status' => 'check_status',
],
'store' => [
'store_title' => 'title',
],
'employee' => [
'employee_name' => 'name',
'employee_search' => 'search',
],
];
public function employeeId($key)
{
$this->where('employee_id', $key);
@ -32,9 +45,4 @@ class EmployeeSignRepairFilter extends ModelFilter
$end = Carbon::createFromTimestamp(data_get($dates, 1, time()))->endOfDay();
$this->whereBetween('date', [$start, $end]);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
}

View File

@ -10,6 +10,9 @@ class HolidayApplyFilter extends ModelFilter
protected $drop_id = false;
public $relations = [
'workflow' => [
'check_status' => 'check_status',
],
'store' => [
'store_title' => 'title',
],
@ -28,9 +31,4 @@ class HolidayApplyFilter extends ModelFilter
{
$this->where('type_id', $key);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
}

View File

@ -10,6 +10,9 @@ class OfficalBusinessFilter extends ModelFilter
protected $drop_id = false;
public $relations = [
'workflow' => [
'check_status' => 'check_status',
],
'store' => [
'store_title' => 'title',
],

View File

@ -0,0 +1,21 @@
<?php
namespace App\Admin\Filters;
use EloquentFilter\ModelFilter;
use Carbon\Carbon;
class WorkflowCheckFilter extends ModelFilter
{
protected $drop_id = false;
public function employeeId($key)
{
$this->where('employee_id', $key);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
}

View File

@ -3,7 +3,7 @@
namespace App\Admin\Services;
use App\Admin\Filters\EmployeeSignRepairFilter;
use App\Models\{EmployeeSignRepair, WorkflowLog};
use App\Models\{EmployeeSignRepair, WorkflowCheck};
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
@ -12,7 +12,7 @@ use App\Enums\CheckStatus;
class EmployeeSignRepairService extends BaseService
{
protected array $withRelationships = ['employee', 'store'];
protected array $withRelationships = ['employee', 'store', 'workflow'];
protected string $modelName = EmployeeSignRepair::class;
@ -46,16 +46,17 @@ class EmployeeSignRepairService extends BaseService
public function preDelete(array $ids)
{
// 删除审核流程记录
WorkflowLog::where('subject_type', (new WorkflowLog)->getMorphClass())->whereIn('subject_id', $ids)->delete();
WorkflowCheck::where('subject_type', (new WorkflowLog)->getMorphClass())->whereIn('subject_id', $ids)->delete();
return true;
}
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
// todo
$unique = Rule::unique('employee_sign_repairs', 'date')
->where('employee_id', data_get($data, 'employee_id', $model?->employee_id))
->where('repair_type', data_get($data, 'repair_type', $model?->repair_type))
->whereIn('check_status', [CheckStatus::Success, CheckStatus::Processing, CheckStatus::None]);
->where('repair_type', data_get($data, 'repair_type', $model?->repair_type));
$createRules = [
'employee_id' => ['required'],
'repair_type' => ['required'],

View File

@ -11,7 +11,7 @@ use Carbon\Carbon;
class HolidayApplyService extends BaseService
{
protected array $withRelationships = ['store', 'employee', 'type'];
protected array $withRelationships = ['store', 'employee', 'type', 'workflow'];
protected string $modelName = HolidayApply::class;
@ -35,9 +35,7 @@ class HolidayApplyService extends BaseService
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (HolidayApply::where('employee_id', data_get($data, 'employee_id', $model?->employee_id))->where(fn($q) => $q->whereBetween('start_at', [$data['start_at'], $data['end_at']])->orWhereBetween('end_at', [$data['start_at'], $data['end_at']]))->exists()) {
return '该时间段已经申请过了';
}
// todo
$createRules = [
'employee_id' => ['required'],
'type_id' => ['required'],

View File

@ -11,7 +11,7 @@ use Carbon\Carbon;
class OfficalBusinessService extends BaseService
{
protected array $withRelationships = ['store', 'employee'];
protected array $withRelationships = ['store', 'employee', 'workflow'];
protected string $modelName = OfficalBusiness::class;
@ -36,9 +36,7 @@ class OfficalBusinessService extends BaseService
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (OfficalBusiness::where('employee_id', data_get($data, 'employee_id', $model?->employee_id))->where(fn($q) => $q->whereBetween('start_at', [$data['start_at'], $data['end_at']])->orWhereBetween('end_at', [$data['start_at'], $data['end_at']]))->exists()) {
return '该时间段已经申请过了';
}
// todo
$createRules = [
'employee_id' => ['required'],
'start_at' => ['required'],

View File

@ -11,7 +11,7 @@ use Carbon\Carbon;
class OvertimeApplyService extends BaseService
{
protected array $withRelationships = ['store', 'employee'];
protected array $withRelationships = ['store', 'employee', 'workflow'];
protected string $modelName = OvertimeApply::class;
@ -38,9 +38,7 @@ class OvertimeApplyService extends BaseService
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (OvertimeApply::where('employee_id', data_get($data, 'employee_id', $model?->employee_id))->where(fn($q) => $q->whereBetween('start_at', [$data['start_at'], $data['end_at']])->orWhereBetween('end_at', [$data['start_at'], $data['end_at']]))->exists()) {
return '该时间段已经申请过了';
}
// todo
$createRules = [
'employee_id' => ['required'],
'start_at' => ['required'],

View File

@ -3,7 +3,7 @@
namespace App\Admin\Services;
use App\Enums\{CheckType, CheckStatus};
use App\Models\{Employee, Store, Keyword, Workflow, WorkflowLog};
use App\Models\{Employee, Store, Keyword, Workflow, WorkflowCheck, WorkflowLog};
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Contracts\Checkable;
@ -21,30 +21,34 @@ class WorkFlowService extends BaseService
* 1. 待审核 Model 实现 Checkable
* 2. 生成全部的审核流程
*
* @param Checkable $subject 待审核记录
* @param WorkflowCheck $check 待审核记录
* @param Employee $user 申请人
*
* @return boolean true: 成功, false: 失败, $this->getError(): 错误消息
*/
public function apply(Checkable $subject, Employee $user)
public function apply(WorkflowCheck $check, Employee $user)
{
if ($subject->check_status === CheckStatus::Success->value) {
if ($check->check_status === CheckStatus::Success->value) {
return $this->setError('已经审核通过');
}
if ($subject->check_status === CheckStatus::Processing->value) {
if ($check->check_status === CheckStatus::Processing->value) {
return $this->setError('正在审核中');
}
$workflow = Workflow::where('key', $subject->getCheckKey())->first();
$workflow = Workflow::where('key', $check->key)->first();
// 没有配置审核流程, 直接通过
if (!$workflow || !$workflow->config) {
$subject->checkSuccess();
$this->success();
return true;
}
$jobs = Keyword::where('parent_key', 'job')->get();
$config = collect($workflow->config)->sortBy('sort');
$batchId = WorkflowLog::where('subject_type', $subject->getMorphClass())->where('subject_id', $subject->id)->max('batch_id') + 1;
$subjectData = $subject->toArray();
$batchId = $check->logs()->max('batch_id') + 1;
$check->update([
'subject_data' => $check->subject->toArray(),
'check_status' => CheckStatus::Processing,
'employee_id' => $user->id,
]);
foreach($config as $item) {
$checkValue = '';
$checkName = '';
@ -69,19 +73,36 @@ class WorkFlowService extends BaseService
return $this->setError('未知的审核类型: ' . $item['type']);
break;
}
$subject->workflows()->create([
$check->logs()->create([
'batch_id' => $batchId,
'check_type' => $item['type'],
'check_value' => $checkValue,
'check_name' => $checkName,
'user_id' => $user->id,
'subject_data' => $subjectData,
'sort' => $item['sort']
]);
}
$subject->checkApply();
// 开启第一个审核流程
$this->next($subject);
$this->next($check);
return true;
}
public function success(WorkflowCheck $check, array $options = [])
{
$check->update([
'check_status' => CheckStatus::Success,
'checked_at' => data_get($options, 'checked_at', now()),
'check_remarks' => data_get($options, 'remarks')
]);
return true;
}
public function fail(WorkflowCheck $check, array $options = [])
{
$check->update([
'check_status' => CheckStatus::Fail,
'check_remarks' => data_get($options, 'remarks'),
'checked_at' => data_get($options, 'checked_at', now()),
]);
return true;
}
@ -91,10 +112,13 @@ class WorkFlowService extends BaseService
* 2. 更新申请记录的状态
*
*/
public function cancel(Checkable $subject)
public function cancel(WorkflowCheck $check)
{
$subject->workflows()->whereIn('check_status', [CheckStatus::None, CheckStatus::Processing])->delete();
$subject->checkCancel();
$check->logs()->whereIn('check_status', [CheckStatus::None, CheckStatus::Processing])->delete();
$check->update([
'check_status' => CheckStatus::Cancel,
]);
return true;
}
@ -121,42 +145,39 @@ class WorkFlowService extends BaseService
$attributes['check_user_id'] = $user->id;
$log->update($attributes);
$subject = $log->subject;
// 审核未通过, 删除剩下的流程
if (!$status) {
WorkflowLog::where('subject_type', $log->subject_type)->where('subject_id', $log->subject_id)->where('check_status', CheckStatus::None)->delete();
$subject->checkFail($options);
return true;
$check = $log->check;
if ($status) {
return $this->next($check);
} else {
return $this->fail($check, $options);
}
return $this->next($subject);
}
/**
* 开启下一个审核流程
*
* @param Checkable $subject
* @param WorkflowCheck $check
* @return boolean
*/
public function next(Checkable $subject)
public function next(WorkflowCheck $check)
{
$log = $subject->workflows()->where('check_status', CheckStatus::None)->orderBy('sort')->first();
$log = $check->logs()->where('check_status', CheckStatus::None)->orderBy('sort')->first();
if ($log) {
$log->update(['check_status' => CheckStatus::Processing]);
// 申请人自动审核通过
if ($this->authCheck($subject->employee, $log)) {
return $this->check($subject->employee, $log, true, ['remarks' => '自动审核通过']);
if ($this->authCheck($check->employee, $log)) {
return $this->check($check->employee, $log, true, ['remarks' => '自动审核通过']);
}
} else {
// 没有审核流程了
$subject->checkSuccess();
// 没有审核流程了, 审核完成
return $this->success($check);
}
return true;
}
/**
* 员工是否有权限审核
* 是否有权限审核
*/
public function authCheck(Employee $user, WorkflowLog $log)
{

View File

@ -14,5 +14,5 @@ interface Checkable
public function getCheckKey();
public function workflows();
public function workflow();
}

View File

@ -29,7 +29,7 @@ class EmployeeSignLog extends Model
public function modelFilter()
{
return \App\Admin\Filters\EmployeeSignRepairFilter::class;
return \App\Admin\Filters\EmployeeSignLogFilter::class;
}
public function store()

View File

@ -3,33 +3,31 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Enums\{SignTime, CheckStatus};
use App\Enums\{SignTime};
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
use App\Contracts\Checkable;
use App\Traits\HasCheckable;
/**
* 补卡申请
*/
class EmployeeSignRepair extends Model implements Checkable
class EmployeeSignRepair extends Model
{
use HasDateTimeFormatter, Filterable, HasCheckable;
protected $table = 'employee_sign_repairs';
protected $fillable = ['date', 'store_id', 'employee_id', 'reason', 'repair_type', 'check_status', 'checked_at', 'check_remarks'];
protected $fillable = ['date', 'store_id', 'employee_id', 'reason', 'repair_type'];
protected $casts = [
'date' => 'date:Y-m-d',
'checked_at' => 'datetime',
'repair_type' => SignTime::class,
'check_status' => CheckStatus::class,
];
public function modelFilter()
{
return EmployeeSignLogFilter::class;
return \App\Admin\Filters\EmployeeSignRepairFilter::class;
}
public function store()

View File

@ -3,7 +3,6 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Contracts\Checkable;
use App\Traits\HasCheckable;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
@ -12,16 +11,15 @@ use App\Enums\{CheckStatus};
/**
* 请假申请
*/
class HolidayApply extends Model implements Checkable
class HolidayApply extends Model
{
use Filterable, HasCheckable, HasDateTimeFormatter;
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'type_id', 'reason', 'check_status', 'checked_at', 'check_remarks'];
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'type_id', 'reason'];
protected $casts = [
'start_at' => 'date:Y-m-d',
'end_at' => 'date:Y-m-d',
'check_status' => CheckStatus::class,
];
public function modelFilter()
@ -34,6 +32,11 @@ class HolidayApply extends Model implements Checkable
return $this->belongsTo(Store::class, 'store_id');
}
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
public function type()
{
// holiday_type

View File

@ -3,7 +3,6 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Contracts\Checkable;
use App\Traits\HasCheckable;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
@ -12,23 +11,27 @@ use App\Enums\{CheckStatus};
/**
* 出差报备
*/
class OfficalBusiness extends Model implements Checkable
class OfficalBusiness extends Model
{
use HasCheckable, HasDateTimeFormatter, Filterable;
protected $table = 'offical_business';
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'address', 'reason', 'check_status', 'checked_at', 'check_remarks'];
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'address', 'reason'];
protected $casts = [
'start_at' => 'date:Y-m-d',
'end_at' => 'date:Y-m-d',
'check_status' => CheckStatus::class,
];
public function modelFilter()
{
return App\Admin\Filters\OfficalBusinessFilter::class;
return \App\Admin\Filters\OfficalBusinessFilter::class;
}
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
public function store()

View File

@ -3,7 +3,6 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Contracts\Checkable;
use App\Traits\HasCheckable;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
@ -12,22 +11,26 @@ use App\Enums\{CheckStatus};
/**
* 加班申请
*/
class OvertimeApply extends Model implements Checkable
class OvertimeApply extends Model
{
use Filterable, HasDateTimeFormatter, HasCheckable;
protected $fillable = ['store_id', 'employee_id', 'date', 'start_at', 'end_at', 'hours', 'reason', 'check_status', 'checked_at', 'check_remarks'];
protected $fillable = ['store_id', 'employee_id', 'date', 'start_at', 'end_at', 'hours', 'reason'];
protected $casts = [
'date' => 'date:Y-m-d',
'start_at' => 'datetime',
'end_at' => 'datetime',
'check_status' => CheckStatus::class,
];
public function modelFilter()
{
return App\Admin\Filters\HolidayApplyFilter::class;
return \App\Admin\Filters\HolidayApplyFilter::class;
}
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
public function store()

View File

@ -0,0 +1,48 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\Employee;
use App\Enums\CheckStatus;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
/**
* 审核申请
*/
class WorkflowCheck extends Model
{
use HasDateTimeFormatter, Filterable;
protected $fillable = ['subject_type', 'subject_id', 'subject_data', 'key', 'employee_id', 'check_status', 'checked_at', 'check_remarks'];
protected $casts = [
'subject_data' => 'json',
'check_status' => CheckStatus::class,
'checked_at' => 'datetime',
];
public function modelFilter()
{
return \App\Admin\Filters\WorkflowCheckFilter::class;
}
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
public function subject()
{
// 定义反向关联
// $this->morphMany(WorkflowLog::class, 'subject');
return $this->morphTo();
}
public function logs()
{
return $this->hasMany(WorkflowLog::class, 'check_id');
}
}

View File

@ -11,28 +11,25 @@ use Illuminate\Database\Eloquent\Model;
*/
class WorkflowLog extends Model
{
protected $fillable = ['batch_id', 'check_type', 'check_value', 'check_name', 'subject_type', 'subject_id', 'subject_data', 'check_user_id', 'checked_at', 'remarks', 'check_status', 'sort'];
protected $fillable = ['check_id', 'batch_id', 'check_type', 'check_value', 'check_name', 'check_user_id', 'checked_at', 'remarks', 'check_status', 'sort'];
protected $casts = [
'check_type' => CheckType::class,
'check_status' => CheckStatus::class,
'subject_data' => 'json',
];
public function check()
{
return $this->belongsTo(WorkflowCheck::class, 'check_id');
}
public function checkUser()
{
return $this->belongsTo(Employee::class, 'check_user_id');
}
public function subject()
{
// 定义反向关联
// $this->morphMany(WorkflowLog::class, 'subject');
return $this->morphTo();
}
public function scopeSort($q)
{
return $q->orderBy('batch_id')->orderBy('sort');
return $q->orderBy('sort');
}
}

View File

@ -6,11 +6,6 @@ use App\Enums\{CheckStatus};
trait HasCheckActions
{
public function getMorphAlias()
{
// return (new EmployeeSignRepair)->getMorphClass();
return '';
}
public function applyAction()
{
return amisMake()
@ -18,11 +13,11 @@ trait HasCheckActions
->label('发起审核')
->level('link')
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/apply'))->method('post')->data([
'subject_type' => $this->getMorphAlias(),
'subject_id' => '${id}'
'id' => '${workflow.id}',
'user' => '${employee_id}',
]))
->confirmText(__('admin.confirm'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}');
->visibleOn('${OR(workflow.check_status == '.CheckStatus::None->value.', workflow.check_status == '.CheckStatus::Cancel->value.', workflow.check_status == '.CheckStatus::Fail->value.')}');
}
@ -33,11 +28,10 @@ trait HasCheckActions
->label('取消审核')
->level('link')
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/cancel'))->method('post')->data([
'subject_type' => $this->getMorphAlias(),
'subject_id' => '${id}',
'id' => '${workflow.id}'
]))
->confirmText(__('admin.confirm'))
->visibleOn('${check_status == '.CheckStatus::Processing->value.'}');
->visibleOn('${workflow.check_status == '.CheckStatus::Processing->value.'}');
}
public function succesAction()
@ -59,7 +53,7 @@ trait HasCheckActions
->level('link')
->dialog(amisMake()->Dialog()->title('审核不通过')->body(
amisMake()->Form()->title('')->api('post:'.admin_url('api/workflow/fail'))->body([
amisMake()->HiddenControl()->name('id'),
amisMake()->HiddenControl()->name('id')->value('${id}'),
amisMake()->TextControl()->name('remarks')->label(__('workflow_log.remarks'))->required(),
])
))

View File

@ -3,83 +3,33 @@
namespace App\Traits;
use App\Enums\CheckStatus;
use App\Models\{WorkflowLog, Employee};
use App\Models\{WorkflowCheck, Employee};
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
trait HasCheckable
{
public function checkApply()
protected $check_key = '';
protected static function booted(): void
{
$this->update([
'check_status' => CheckStatus::Processing,
'checked_at' => null,
'check_remarks' => '',
]);
}
/**
* 审核通过
*
* @param array $options {remarks: 不通过原因, time: 审核时间(默认当前时间)}
*/
public function checkSuccess(array $options = [])
{
$attributes = ['check_status' => CheckStatus::Success];
$attributes['checked_at'] = data_get($options, 'time', now());
$attributes['check_remarks'] = data_get($options, 'remarks');
$this->update($attributes);
}
/**
* 审核未通过
*
* @param array $options {remarks: 不通过原因, time: 审核时间(默认当前时间)}
*/
public function checkFail(array $options = [])
{
$attributes = ['check_status' => CheckStatus::Fail];
$attributes['checked_at'] = data_get($options, 'time', now());
$attributes['check_remarks'] = data_get($options, 'remarks');
$this->update($attributes);
}
public function checkCancel(array $options = [])
{
$this->update([
'check_status' => CheckStatus::Cancel,
'checked_at' => data_get($options, 'time', now()),
]);
static::created(function ($model) {
// 创建审核申请
$model->workflow()->create([
'key' => $model->getCheckKey(),
]);
});
}
public function getCheckKey()
{
return Str::snake(class_basename(__CLASS__));
return $this->check_key ?: Str::snake(class_basename(__CLASS__));
}
/**
* 关联审核流水
*/
public function workflows()
public function workflow()
{
return $this->morphMany(WorkflowLog::class, 'subject');
}
/**
* 关联申请人
*/
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
/**
* 查询审核通过的记录
*
* @param Builder $q
*/
public function scopeChecked(Builder $q): Builder
{
return $q->where('check_status', CheckStatus::Success);
return $this->morphOne(WorkflowCheck::class, 'subject');
}
}

View File

@ -22,14 +22,27 @@ return new class extends Migration
$table->comment('审核流程');
});
Schema::create('workflow_checks', function (Blueprint $table) {
$table->id();
$table->string('key')->comment('审核流程的key');
$table->morphs('subject');
$table->json('subject_data')->nullable('审核内容');
$table->foreignId('employee_id')->nullable()->comment('申请人, employees.id');
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->timestamps();
$table->comment('审核申请');
});
Schema::create('workflow_logs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('batch_id')->comment('批次号(为同一批审核流程设置一个ID)');
$table->unsignedInteger('batch_id')->default(1)->comment('批次号(每次申请, 批次号递增)');
$table->foreignId('check_id')->comment('审核申请, workflow_checks.id');
$table->string('check_type')->comment('审核类型{job, user}');
$table->string('check_value')->comment('审核类型值');
$table->string('check_name')->comment('审核名称(展示用)');
$table->nullableMorphs('subject');
$table->json('subject_data')->nullable('审核内容');
$table->unsignedBigInteger('check_user_id')->nullable()->comment('实际审核人(admin_users.id)');
$table->timestamp('checked_at')->nullable()->comment('审核时间');
$table->string('remarks')->nullable()->comment('审核备注');

View File

@ -3,7 +3,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Enums\CheckStatus;
return new class extends Migration
{
@ -55,11 +55,7 @@ return new class extends Migration
$table->foreignId('store_id')->comment('门店, stores.id');
$table->foreignId('employee_id')->comment('员工, employees.id');
$table->string('reason')->comment('补卡原因');
$table->unsignedInteger('repair_type')->default(1)->comment('上班/下班 补卡');
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->unsignedInteger('repair_type')->default(1)->comment('上班/下班');
$table->timestamps();

View File

@ -3,7 +3,6 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Enums\CheckStatus;
return new class extends Migration
{
@ -20,10 +19,6 @@ return new class extends Migration
$table->datetime('end_at')->comment('开始时间');
$table->string('type_id')->comment('类型(holiday_type), keywords.key');
$table->string('reason')->comment('事由');
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->timestamps();
$table->comment('请假申请');
});

View File

@ -3,7 +3,6 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Enums\CheckStatus;
return new class extends Migration
{
@ -21,10 +20,6 @@ return new class extends Migration
$table->timestamp('end_at')->comment('开始时间');
$table->unsignedInteger('hours')->comment('时长');
$table->string('reason')->nullable()->comment('事由');
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->timestamps();
});
}

View File

@ -19,10 +19,6 @@ return new class extends Migration
$table->timestamp('end_at')->comment('开始时间');
$table->string('address')->comment('目的地');
$table->string('reason')->nullable()->comment('事由');
$table->unsignedInteger('check_status')->default(App\Enums\CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->timestamps();
$table->comment('出差报备');
});

View File

@ -16,15 +16,15 @@ class EmployeeSeeder extends Seeder
*/
public function run(): void
{
DB::table('employee_jobs')->truncate();
Employee::where('admin_user_id', '!=', 1)->delete();
(new EmployeeFactory)->count(100)->create();
// DB::table('employee_jobs')->truncate();
// Employee::where('admin_user_id', '!=', 1)->delete();
// (new EmployeeFactory)->count(100)->create();
Store::truncate();
Store::factory()->count(10)->create();
// Store::truncate();
// Store::factory()->count(10)->create();
// EmployeeSign::truncate();
// EmployeeSignLog::truncate();
// EmployeeSignLog::factory()->count(100)->create();
EmployeeSign::truncate();
EmployeeSignLog::truncate();
EmployeeSignLog::factory()->count(100)->create();
}
}