model = new Assessment(); } /** * 绩效考核列表 */ public function index(Request $request): Response { if ($request->isAjax()) { $param = $request->get(); if ($param['subResource'] ?? false) { $keyword = trim($param['keyword'] ?? ''); $subResource = []; switch ($param['subResource']) { case 'user': $subResource = $this->option(AdminUser::where('name', 'like', '%' . $keyword . '%')->limit(20)->get(), 'nickname'); break; default: return sparkSuccess(dataReturn(-1, '未知子资源')); } return sparkSuccess(dataReturn(0, '获取子资源成功', $subResource)); } $param['pagination'] ??= ['page' => 1, 'limit' => 10]; $query = $this->model::with(['user', 'role']); return sparkSuccess(dataReturn(0, '获取成功', $query->paginate($param['pagination']['limit'], ['*'], 'page', $param['pagination']['page']))); } return view('safebasic/perfstandard/perfAssessment/index'); } /** * 查绩效考核 */ public function show(Request $request, $id): Response { $show = $this->model::with(['user', 'role',]) ->with( 'runs', fn($query) => $query->with('standard', 'rule', 'records') ) ->find($id); return view('safebasic/perfstandard/perfAssessment/show', ['show' => $show]); } /** * 增绩效考核 */ public function store(Request $request): Response { $data = $request->post(); /** @var AdminUser $user */ $user = AdminUser::with('role')->find($data['user_id']); $data['role_id'] = $user->role->id; // 过滤黑名单 $blacklist = ['']; $data = Arr::except($data, $blacklist); try { Db::beginTransaction(); $this->model = $this->model::create($data); // 执行初始化 $assessmentSchedulerService = new AssessmentSchedulerService(); $assessmentInitializationService = new AssessmentInitializationService($assessmentSchedulerService); $assessmentInitializationService->initializeAssessment($this->model); Db::commit(); return sparkSuccess(dataReturn(0, '添加成功')); } catch (\Exception $e) { Db::rollBack(); throw $e; } } /** * 改绩效考核 */ public function update(Request $request, int $id): Response { $data = $request->post(); // 过滤黑名单 $blacklist = ['']; $data = Arr::except($data, $blacklist); try { Db::beginTransaction(); $this->model::where('id', $id)->update($data); // $this->model = $this->model::find($id); // $this->model->update($data); Db::commit(); return sparkSuccess(dataReturn(0, '修改成功')); } catch (\Exception $e) { Db::rollBack(); throw $e; } } /** * 删绩效考核 */ public function destroy(Request $request, int $id): Response { try { Db::beginTransaction(); $this->model = $this->model->find($id); $this->model->applyDelete(); Db::commit(); return sparkSuccess(dataReturn(0, '删除成功')); } catch (\Exception $e) { Db::rollBack(); throw $e; } } /** * 完成绩效考核 */ public function complete(Request $request, int $id): Response { $data = $request->post(); try { Db::beginTransaction(); /** @var RuleRunRecord $ruleRunRecord */ $ruleRunRecord = RuleRunRecord::find($data['rule_run_record_id']); $action = '上传'; $attachment = $ruleRunRecord->attachment; $attachment[0] ? $attachment[] = $data['attachment'] : $attachment = [$data['attachment']]; $ruleRunRecord->update([ 'attachment' => $attachment, 'result' => '已完成' ]); Db::commit(); return sparkSuccess(dataReturn(0, $action . '成功')); } catch (\Exception $e) { Db::rollBack(); throw $e; } } /** * 结算绩效考核 */ public function settle(Request $request, int $id): Response { $data = $request->post(); try { Db::beginTransaction(); $assessmentSchedulerService = new AssessmentSchedulerService(); $assessmenttSettlementService = new AssessmentSettlementService($assessmentSchedulerService); if (isset ($data['standard_id'])) { /** @var Standard $standard */ $standard = Standard::find($data['standard_id']); $assessmenttSettlementService->settleStandardImmediately($standard); } Db::commit(); return sparkSuccess(dataReturn(0, '结算成功')); } catch (\Exception $e) { Db::rollBack(); throw $e; } } } /** * 考核调度服务 */ class AssessmentSchedulerService { /** * 获取下次执行规则时间 * * 根据给定的执行频次和当前时间,计算下次执行的具体时间点 * * @param string $frequency 执行频次,支持每年一次、每半年一次、每季度一次、每月一次、每周一次、每天一次 * @param string $now 当前时间,格式为可被Carbon解析的日期字符串 * @return string 返回下次执行的时间,格式为日期时间字符串 * @throws \Exception 如果传入的频次不属于预定义的频次之一,则抛出异常 */ public function getNextRuleRunTime(string $frequency, string $now): string { $now = Carbon::parse($now); // 解析当前时间 switch ($frequency) { // 根据不同的频次,计算下次执行的时间 case '每年一次': return $now->addYear()->toDateTimeString(); case '每半年一次': return $now->addMonths(6)->toDateTimeString(); case '每季度一次': return $now->addMonths(3)->toDateTimeString(); case '每月一次': return $now->addMonth()->toDateTimeString(); case '每周一次': return $now->addWeek()->toDateTimeString(); case '每天一次': return $now->addDay()->toDateTimeString(); default: // 如果传入的频次不是预定义的频次之一,则抛出异常 throw new \Exception('频次异常', -1); } } } /** * 考核初始化服务 */ class AssessmentInitializationService { /** @var AssessmentSchedulerService $assessmentSchedulerService 考核调度服务实例 */ protected AssessmentSchedulerService $assessmentSchedulerService; /** * 构造函数,注入考核调度服务 * * @param AssessmentSchedulerService $assessmentSchedulerService 考核调度服务实例 */ public function __construct(AssessmentSchedulerService $assessmentSchedulerService) { $this->assessmentSchedulerService = $assessmentSchedulerService; } /** * 初始化考核 * 为指定的考核关联相应的标准和规则,并为这些规则创建运行实例。 * * @param Assessment $assessment 考核实例 * @throws \Exception 如果用户所属的岗位没有设置标准,抛出异常 */ public function initializeAssessment(Assessment $assessment): void { // 获取用户的岗位标准 $standards = $assessment->role->standards; // 检查岗位是否有标准 if ($standards->isEmpty()) { throw new \Exception('岗位标准不存在'); } // 初始化规则集合 $rulesToInitialize = $standards->flatMap( fn(Standard $standard) => $standard->rules->map(fn(Rule $rule) => [ 'assessment' => $assessment, 'standard' => $standard, 'rule' => $rule ]) )->all(); // 批量处理之前收集到的所有规则初始化需求 foreach ($rulesToInitialize as $ruleData) { // 对每个规则执行初始化操作 $this->initializeRule( $ruleData['assessment'], $ruleData['standard'], $ruleData['rule'] ); } } /** * 初始化考核规则 * 为指定的规则创建一个运行实例,并初始化其记录。 * * @param Assessment $assessment 考核实例 * @param Standard $standard 标准实例 * @param Rule $rule 规则实例 */ private function initializeRule(Assessment $assessment, Standard $standard, Rule $rule): void { // 获取当前时间 $time = now(); $nextRunTime = $this->assessmentSchedulerService->getNextRuleRunTime($rule->frequency, $time); // 创建规则运行实例 $runner = $rule->runs()->create([ 'assessment_id' => $assessment->id, 'standard_id' => $standard->id, 'next_run_time' => $nextRunTime, 'create_time' => $time, ]); // 创建规则运行记录 $runner->records()->create([ 'end_time' => $nextRunTime ]); } } /** * 考核结算服务类,提供考核结算相关功能。 */ class AssessmentSettlementService { // 考核调度服务 protected $assessmentSchedulerService; /** * 构造函数,注入考核调度服务 * * @param AssessmentSchedulerService $assessmentSchedulerService 考核调度服务实例 */ public function __construct(AssessmentSchedulerService $assessmentSchedulerService) { $this->assessmentSchedulerService = $assessmentSchedulerService; } /** * 结算指定考核。 * * @param Assessment $assessment 待结算的考核对象。 * @param bool $isImmediate 是否立即结算 */ public function settleAssessment(Assessment $assessment, $isImmediate = false): void { $this->settleStandards($assessment->role->standards, $isImmediate); } /** * 批量结算指定标准。 * * @param Collection $standards 待结算的标准集合。 * @param bool $isImmediate 是否立即结算 */ public function settleStandards(Collection $standards, bool $isImmediate): void { $standards->each(function (Standard $standard) use ($isImmediate) { if ($isImmediate) { $this->settleStandardImmediately($standard); } else { $this->settleStandard($standard); } }); } /** * 结算指定标准。 * * @param Standard $standard 待结算的标准对象。 */ public function settleStandard(Standard $standard): void { // 获取当前时间 $time = now(); // 查询需结算的规则运行记录 $ruleRuns = $standard->runs()->with(['assessment', 'rule']) ->with( 'records', fn($query) => $query->where([ ['end_time', '<=', $time], ['is_settled', 0] ]) )->get(); $this->processRuleRuns($ruleRuns); } /** * 立即结算指定标准。 * * @param Standard $standard 待结算的标准对象。 */ public function settleStandardImmediately(Standard $standard): void { // 获取当前时间 $time = now(); // 查询需结算的规则运行记录 $ruleRuns = $standard->runs()->with(['assessment', 'rule']) ->with( 'records', fn($query) => $query->where([ ['create_time', '<=', $time], ['is_settled', 0] ]) )->get(); $this->processRuleRuns($ruleRuns); } /** * 处理规则运行对象。 * * @param Collection $ruleRuns 规则运行对象集合。 */ private function processRuleRuns(Collection $ruleRuns): void { $ruleRunRecordsToSettle = $ruleRuns->flatMap( fn(RuleRun $ruleRun) => $ruleRun->records->map( fn(RuleRunRecord $ruleRunRecord) => [ 'assessment' => $ruleRun->assessment, 'rule' => $ruleRun->rule, 'ruleRun' => $ruleRun, 'ruleRunRecord' => $ruleRunRecord ] ) ); // 遍历并结算每条规则运行记录 foreach ($ruleRunRecordsToSettle as $ruleRunRecordData) { $this->settleRuleRunRecord( $ruleRunRecordData['assessment'], $ruleRunRecordData['rule'], $ruleRunRecordData['ruleRun'], $ruleRunRecordData['ruleRunRecord'] ); } } /** * 结算指定的规则运行记录。 * * @param Assessment $assessment 相关联的考核对象。 * @param Rule $rule 相关联的规则对象。 * @param RuleRun $ruleRun 相关联的规则运行对象。 * @param RuleRunRecord $ruleRunRecord 相关联的规则运行记录对象。 */ private function settleRuleRunRecord(Assessment $assessment, Rule $rule, RuleRun $ruleRun, RuleRunRecord $ruleRunRecord): void { // 获取本次规则运行的开始时间 $time = $ruleRun->next_run_time; // 计算下一次规则运行的时间 $nextRunTime = $this->assessmentSchedulerService->getNextRuleRunTime($rule->frequency, $time); // 如果规则运行记录的结果不是“已完成”,则更新为“未完成”并相应减少考核得分 if ($ruleRunRecord->result != '已完成') { $ruleRunRecord->score = $rule->score; $ruleRunRecord->result = '未完成'; $assessment->score_reduce += $ruleRunRecord->score; $assessment->calculateScore()->save(); } // 标记该规则运行记录为已结算 $ruleRunRecord->update(['is_settled' => 1]); // 创建新的规则运行记录,以记录结束时间 $ruleRun->records()->create([ 'end_time' => $nextRunTime, 'create_time' => $ruleRun->next_run_time ]); // 更新规则的下一次运行时间 $ruleRun->update(['next_run_time' => $nextRunTime]); } }