diff --git a/niucloud/app/adminapi/controller/school_approval/Config.php b/niucloud/app/adminapi/controller/school_approval/Config.php new file mode 100644 index 00000000..7cc04864 --- /dev/null +++ b/niucloud/app/adminapi/controller/school_approval/Config.php @@ -0,0 +1,192 @@ +service = new SchoolApprovalConfigService(); + } + + /** + * 获取审批流配置列表 + */ + public function lists() + { + $page = input('page', 1); + $limit = input('limit', 10); + $status = input('status', ''); + + $where = []; + if ($status !== '') { + $where[] = ['status', '=', intval($status)]; + } + + $config_name = input('config_name', ''); + if (!empty($config_name)) { + $where[] = ['config_name', 'like', "%{$config_name}%"]; + } + + $data = $this->service->getList($where, $page, $limit); + + return success($data); + } + + /** + * 获取审批流配置详情 + */ + public function info() + { + $id = input('id', 0); + if (empty($id)) { + return error('参数错误'); + } + + $info = $this->service->getInfo($id); + if (empty($info)) { + return error('审批流配置不存在'); + } + + return success($info); + } + + /** + * 添加审批流配置 + */ + public function add() + { + $data = Request::only(['config_name', 'description', 'status', 'nodes']); + + // 验证参数 + if (empty($data['config_name'])) { + return error('配置名称不能为空'); + } + + if (empty($data['nodes']) || !is_array($data['nodes'])) { + return error('至少需要添加一个审批节点'); + } + + // 验证节点数据 + foreach ($data['nodes'] as $node) { + if (empty($node['node_name'])) { + return error('节点名称不能为空'); + } + + if (empty($node['approver_type'])) { + return error('审批人类型不能为空'); + } + + if (empty($node['approver_ids'])) { + return error('审批人不能为空'); + } + } + + // 设置创建人ID + $data['creator_id'] = $this->user_info['uid']; + + try { + $config_id = $this->service->add($data); + return success(['id' => $config_id]); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } + + /** + * 编辑审批流配置 + */ + public function edit() + { + $data = Request::only(['id', 'config_name', 'description', 'status', 'nodes']); + + // 验证参数 + if (empty($data['id'])) { + return error('参数错误'); + } + + if (empty($data['config_name'])) { + return error('配置名称不能为空'); + } + + if (empty($data['nodes']) || !is_array($data['nodes'])) { + return error('至少需要添加一个审批节点'); + } + + // 验证节点数据 + foreach ($data['nodes'] as $node) { + if (empty($node['node_name'])) { + return error('节点名称不能为空'); + } + + if (empty($node['approver_type'])) { + return error('审批人类型不能为空'); + } + + if (empty($node['approver_ids'])) { + return error('审批人不能为空'); + } + } + + try { + $result = $this->service->edit($data); + return success($result); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } + + /** + * 删除审批流配置 + */ + public function delete() + { + $id = input('id', 0); + if (empty($id)) { + return error('参数错误'); + } + + try { + $result = $this->service->delete($id); + return success($result); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } + + /** + * 修改状态 + */ + public function changeStatus() + { + $id = input('id', 0); + $status = input('status', 0); + + if (empty($id)) { + return error('参数错误'); + } + + try { + $result = $this->service->changeStatus($id, $status); + return success($result); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/niucloud/app/adminapi/controller/school_approval/Process.php b/niucloud/app/adminapi/controller/school_approval/Process.php new file mode 100644 index 00000000..d4285f09 --- /dev/null +++ b/niucloud/app/adminapi/controller/school_approval/Process.php @@ -0,0 +1,154 @@ +service = new SchoolApprovalProcessService(); + } + + /** + * 获取审批流程列表 + */ + public function lists() + { + $page = input('page', 1); + $limit = input('limit', 10); + $status = input('approval_status', ''); + + $where = []; + if ($status !== '') { + $where[] = ['approval_status', '=', $status]; + } + + $process_name = input('process_name', ''); + if (!empty($process_name)) { + $where[] = ['process_name', 'like', "%{$process_name}%"]; + } + + // 我发起的审批 + $applicant_id = input('applicant_id', 0); + if (!empty($applicant_id)) { + $where[] = ['applicant_id', '=', $applicant_id]; + } + + // 待我审批的 + $approver_id = input('approver_id', 0); + if (!empty($approver_id)) { + $where[] = ['current_approver_id', '=', $approver_id]; + $where[] = ['approval_status', '=', 'pending']; + } + + $data = $this->service->getList($where, $page, $limit); + + return success($data); + } + + /** + * 获取审批流程详情 + */ + public function info() + { + $id = input('id', 0); + if (empty($id)) { + return error('参数错误'); + } + + $info = $this->service->getInfo($id); + if (empty($info)) { + return error('审批流程不存在'); + } + + return success($info); + } + + /** + * 创建审批流程 + */ + public function create() + { + $data = Request::only(['process_name', 'remarks']); + $config_id = input('config_id', 0); + + // 验证参数 + if (empty($data['process_name'])) { + return error('流程名称不能为空'); + } + + if (empty($config_id)) { + return error('请选择审批流配置'); + } + + // 设置申请人ID + $data['applicant_id'] = $this->user_info['uid']; + + try { + $process_id = $this->service->create($data, $config_id); + return success(['id' => $process_id]); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } + + /** + * 审批 + */ + public function approve() + { + $process_id = input('process_id', 0); + $status = input('status', ''); + $remarks = input('remarks', ''); + + if (empty($process_id)) { + return error('参数错误'); + } + + if (empty($status) || !in_array($status, ['approved', 'rejected'])) { + return error('请选择审批结果'); + } + + try { + $result = $this->service->approve($process_id, $this->user_info['uid'], $status, $remarks); + return success($result); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } + + /** + * 撤销审批流程 + */ + public function cancel() + { + $process_id = input('process_id', 0); + + if (empty($process_id)) { + return error('参数错误'); + } + + try { + $result = $this->service->cancel($process_id, $this->user_info['uid']); + return success($result); + } catch (\Exception $e) { + return error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/niucloud/app/adminapi/route/school_approval.php b/niucloud/app/adminapi/route/school_approval.php new file mode 100644 index 00000000..b58d0de9 --- /dev/null +++ b/niucloud/app/adminapi/route/school_approval.php @@ -0,0 +1,48 @@ +middleware( + [ + app\adminapi\middleware\AdminCheckToken::class, + app\adminapi\middleware\AdminCheckRole::class, + app\adminapi\middleware\AdminLog::class + ] +); + +// 审批流程 +Route::group('school_approval/process', function () { + // 审批流程列表 + Route::get('lists', 'school_approval.Process/lists'); + // 审批流程详情 + Route::get('info', 'school_approval.Process/info'); + // 创建审批流程 + Route::post('create', 'school_approval.Process/create'); + // 审批 + Route::post('approve', 'school_approval.Process/approve'); + // 撤销审批流程 + Route::post('cancel', 'school_approval.Process/cancel'); +})->middleware( + [ + app\adminapi\middleware\AdminCheckToken::class, + app\adminapi\middleware\AdminCheckRole::class, + app\adminapi\middleware\AdminLog::class + ] +); \ No newline at end of file diff --git a/niucloud/app/model/school_approval/SchoolApprovalConfig.php b/niucloud/app/model/school_approval/SchoolApprovalConfig.php new file mode 100644 index 00000000..2d5d4847 --- /dev/null +++ b/niucloud/app/model/school_approval/SchoolApprovalConfig.php @@ -0,0 +1,53 @@ +hasMany(SchoolApprovalConfigNode::class, 'config_id', 'id')->order('sequence', 'asc'); + } +} \ No newline at end of file diff --git a/niucloud/app/model/school_approval/SchoolApprovalConfigNode.php b/niucloud/app/model/school_approval/SchoolApprovalConfigNode.php new file mode 100644 index 00000000..e7292637 --- /dev/null +++ b/niucloud/app/model/school_approval/SchoolApprovalConfigNode.php @@ -0,0 +1,66 @@ +belongsTo(SchoolApprovalConfig::class, 'config_id', 'id'); + } +} \ No newline at end of file diff --git a/niucloud/app/model/school_approval/SchoolApprovalParticipants.php b/niucloud/app/model/school_approval/SchoolApprovalParticipants.php new file mode 100644 index 00000000..6bd373b8 --- /dev/null +++ b/niucloud/app/model/school_approval/SchoolApprovalParticipants.php @@ -0,0 +1,66 @@ +belongsTo(SchoolApprovalProcess::class, 'process_id', 'id'); + } +} \ No newline at end of file diff --git a/niucloud/app/model/school_approval/SchoolApprovalProcess.php b/niucloud/app/model/school_approval/SchoolApprovalProcess.php new file mode 100644 index 00000000..9786d6dd --- /dev/null +++ b/niucloud/app/model/school_approval/SchoolApprovalProcess.php @@ -0,0 +1,60 @@ +hasMany(SchoolApprovalParticipants::class, 'process_id', 'id')->order('sequence', 'asc'); + } +} \ No newline at end of file diff --git a/niucloud/app/model/school_approval_config.sql b/niucloud/app/model/school_approval_config.sql new file mode 100644 index 00000000..423b1d28 --- /dev/null +++ b/niucloud/app/model/school_approval_config.sql @@ -0,0 +1,96 @@ +-- ======================== +-- 数据库结构增强脚本(MySQL) +-- ======================== + +-- 1. 会计期间有效性校验 +ALTER TABLE financial_accounting_periods + ADD COLUMN status ENUM('active', 'closed', 'locked') DEFAULT 'active' COMMENT '期间状态', +ADD COLUMN validation_date DATE COMMENT '最后验证日期'; + +-- 2. 预算表(支持多版本) +CREATE TABLE financial_budgets ( + budget_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '预算ID', + company_id INT NOT NULL COMMENT '公司ID', + period_id INT NOT NULL COMMENT '会计期间ID', + account_id INT NOT NULL COMMENT '科目ID', + budget_version INT DEFAULT 1 COMMENT '版本号', + planned_amount DECIMAL(18,2) COMMENT '计划金额', + approved_date DATE COMMENT '审批日期', + FOREIGN KEY (company_id) REFERENCES financial_companies(company_id), + FOREIGN KEY (period_id) REFERENCES financial_accounting_periods(period_id), + FOREIGN KEY (account_id) REFERENCES financial_chart_of_accounts(account_id) +) COMMENT='财务预算表'; + +-- 3. 科目层级管理增强 +ALTER TABLE financial_chart_of_accounts + ADD COLUMN path VARCHAR(255) COMMENT '层级路径(如0/1001/)', +ADD COLUMN level INT COMMENT '层级深度'; + +-- 4. 数据血缘追踪表 +CREATE TABLE financial_data_trace ( + trace_id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '追踪记录ID', + source_table VARCHAR(50) NOT NULL COMMENT '源表名', + source_id BIGINT NOT NULL COMMENT '源记录ID', + target_table VARCHAR(50) NOT NULL COMMENT '目标表名', + target_id BIGINT NOT NULL COMMENT '目标记录ID', + operation_type ENUM('insert','update','delete') NOT NULL COMMENT '操作类型', + operator VARCHAR(50) COMMENT '操作人', + operation_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间' +) COMMENT='数据操作追踪表'; + +-- 5. 会计期间重叠检查触发器 +DELIMITER // +CREATE TRIGGER trg_check_period_overlap + BEFORE INSERT ON financial_accounting_periods + FOR EACH ROW +BEGIN + IF EXISTS ( + SELECT 1 FROM financial_accounting_periods + WHERE company_id = NEW.company_id + AND period_id != NEW.period_id + AND ( + (NEW.start_date BETWEEN start_date AND end_date) + OR (NEW.end_date BETWEEN start_date AND end_date) + OR (start_date BETWEEN NEW.start_date AND NEW.end_date) + ) + ) THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT = '会计期间时间重叠'; +END IF; +END// +DELIMITER ; + +-- 6. 初始化会计期间状态 +UPDATE financial_accounting_periods +SET status = CASE + WHEN end_date >= CURDATE() THEN 'active' + WHEN end_date < CURDATE() AND start_date > CURDATE() THEN 'locked' + ELSE 'closed' + END; + +-- 7. 创建科目层级维护存储过程 +DELIMITER // +CREATE PROCEDURE sp_refresh_account_hierarchy() +BEGIN +UPDATE financial_chart_of_accounts c + LEFT JOIN ( + SELECT + a.account_id, + CONCAT( + IFNULL((SELECT CONCAT(path, parent_id, '/') FROM financial_chart_of_accounts + WHERE account_id = a.parent_id), '0/'), + a.account_id, '/' + ) AS new_path, + (LENGTH(IFNULL((SELECT path FROM financial_chart_of_accounts + WHERE account_id = a.parent_id), '0/')) - LENGTH(REPLACE(IFNULL( + (SELECT path FROM financial_chart_of_accounts + WHERE account_id = a.parent_id), '0/'), '/', ''))) + 1 + AS new_level + FROM financial_chart_of_accounts a + ) AS sub ON c.account_id = sub.account_id + SET + c.path = sub.new_path, + c.level = sub.new_level +WHERE c.is_active = TRUE; +END// +DELIMITER ; \ No newline at end of file diff --git a/niucloud/app/service/school_approval/SchoolApprovalConfigService.php b/niucloud/app/service/school_approval/SchoolApprovalConfigService.php new file mode 100644 index 00000000..9a3cb1e9 --- /dev/null +++ b/niucloud/app/service/school_approval/SchoolApprovalConfigService.php @@ -0,0 +1,185 @@ +where($where) + ->field($field) + ->order($order) + ->page($page, $limit) + ->select() + ->toArray(); + + $count = (new SchoolApprovalConfig())->where($where)->count(); + + return [ + 'list' => $list, + 'count' => $count + ]; + } + + /** + * 获取审批流配置详情 + * @param int $id + * @return array + */ + public function getInfo(int $id): array + { + $info = (new SchoolApprovalConfig())->with(['nodes'])->where(['id' => $id])->find(); + if (empty($info)) { + return []; + } + + return $info->toArray(); + } + + /** + * 添加审批流配置 + * @param array $data + * @return int + * @throws \Exception + */ + public function add(array $data): int + { + Db::startTrans(); + try { + $config = [ + 'config_name' => $data['config_name'], + 'description' => $data['description'] ?? '', + 'status' => $data['status'] ?? 1, + 'creator_id' => $data['creator_id'] + ]; + + $config_id = (new SchoolApprovalConfig())->insertGetId($config); + + // 添加节点 + if (!empty($data['nodes'])) { + $nodes = []; + foreach ($data['nodes'] as $sequence => $node) { + $nodes[] = [ + 'config_id' => $config_id, + 'node_name' => $node['node_name'], + 'approver_type' => $node['approver_type'], + 'approver_ids' => is_array($node['approver_ids']) ? implode(',', $node['approver_ids']) : $node['approver_ids'], + 'sign_type' => $node['sign_type'] ?? SchoolApprovalConfigNode::SIGN_TYPE_OR, + 'sequence' => $sequence + 1 + ]; + } + + (new SchoolApprovalConfigNode())->insertAll($nodes); + } + + Db::commit(); + return $config_id; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } + + /** + * 编辑审批流配置 + * @param array $data + * @return bool + * @throws \Exception + */ + public function edit(array $data): bool + { + Db::startTrans(); + try { + $config = [ + 'config_name' => $data['config_name'], + 'description' => $data['description'] ?? '', + 'status' => $data['status'] ?? 1 + ]; + + (new SchoolApprovalConfig())->where(['id' => $data['id']])->update($config); + + // 先删除原有节点 + (new SchoolApprovalConfigNode())->where(['config_id' => $data['id']])->delete(); + + // 添加新节点 + if (!empty($data['nodes'])) { + $nodes = []; + foreach ($data['nodes'] as $sequence => $node) { + $nodes[] = [ + 'config_id' => $data['id'], + 'node_name' => $node['node_name'], + 'approver_type' => $node['approver_type'], + 'approver_ids' => is_array($node['approver_ids']) ? implode(',', $node['approver_ids']) : $node['approver_ids'], + 'sign_type' => $node['sign_type'] ?? SchoolApprovalConfigNode::SIGN_TYPE_OR, + 'sequence' => $sequence + 1 + ]; + } + + (new SchoolApprovalConfigNode())->insertAll($nodes); + } + + Db::commit(); + return true; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } + + /** + * 删除审批流配置 + * @param int $id + * @return bool + * @throws \Exception + */ + public function delete(int $id): bool + { + Db::startTrans(); + try { + // 删除配置 + (new SchoolApprovalConfig())->where(['id' => $id])->delete(); + + // 删除节点 + (new SchoolApprovalConfigNode())->where(['config_id' => $id])->delete(); + + Db::commit(); + return true; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } + + /** + * 修改状态 + * @param int $id + * @param int $status + * @return bool + */ + public function changeStatus(int $id, int $status): bool + { + return (new SchoolApprovalConfig())->where(['id' => $id])->update(['status' => $status]) !== false; + } +} \ No newline at end of file diff --git a/niucloud/app/service/school_approval/SchoolApprovalProcessService.php b/niucloud/app/service/school_approval/SchoolApprovalProcessService.php new file mode 100644 index 00000000..f92094f1 --- /dev/null +++ b/niucloud/app/service/school_approval/SchoolApprovalProcessService.php @@ -0,0 +1,289 @@ +where($where) + ->field($field) + ->order($order) + ->page($page, $limit) + ->select() + ->toArray(); + + $count = (new SchoolApprovalProcess())->where($where)->count(); + + return [ + 'list' => $list, + 'count' => $count + ]; + } + + /** + * 获取审批流程详情 + * @param int $id + * @return array + */ + public function getInfo(int $id): array + { + $info = (new SchoolApprovalProcess())->with(['participants'])->where(['id' => $id])->find(); + if (empty($info)) { + return []; + } + + return $info->toArray(); + } + + /** + * 创建审批流程 + * @param array $data + * @param int $config_id 审批配置ID + * @return int + * @throws \Exception + */ + public function create(array $data, int $config_id): int + { + Db::startTrans(); + try { + // 获取审批配置详情 + $config_info = (new SchoolApprovalConfigService())->getInfo($config_id); + if (empty($config_info)) { + throw new Exception('审批配置不存在'); + } + + // 创建审批流程 + $process = [ + 'process_name' => $data['process_name'], + 'applicant_id' => $data['applicant_id'], + 'application_time' => time(), + 'current_approver_id' => 0, // 初始时为0,后面会更新 + 'approval_status' => SchoolApprovalProcess::STATUS_PENDING, + 'remarks' => $data['remarks'] ?? '' + ]; + + $process_id = (new SchoolApprovalProcess())->insertGetId($process); + + // 创建审批参与人 + $participants = []; + foreach ($config_info['nodes'] as $sequence => $node) { + $approver_ids = explode(',', $node['approver_ids']); + foreach ($approver_ids as $approver_id) { + $participants[] = [ + 'process_id' => $process_id, + 'participant_id' => $approver_id, + 'sequence' => $node['sequence'], + 'status' => SchoolApprovalParticipants::STATUS_PENDING, + 'sign_type' => $node['sign_type'] + ]; + } + } + + if (!empty($participants)) { + (new SchoolApprovalParticipants())->insertAll($participants); + + // 更新当前审批人为第一个审批人 + $first_participant = (new SchoolApprovalParticipants()) + ->where(['process_id' => $process_id]) + ->order('sequence', 'asc') + ->find(); + + if (!empty($first_participant)) { + (new SchoolApprovalProcess())->where(['id' => $process_id]) + ->update(['current_approver_id' => $first_participant['participant_id']]); + } + } + + Db::commit(); + return $process_id; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } + + /** + * 审批 + * @param int $process_id 流程ID + * @param int $approver_id 审批人ID + * @param string $status 审批状态 + * @param string $remarks 备注 + * @return bool + * @throws \Exception + */ + public function approve(int $process_id, int $approver_id, string $status, string $remarks = ''): bool + { + Db::startTrans(); + try { + // 获取审批流程 + $process_info = (new SchoolApprovalProcess())->where(['id' => $process_id])->find(); + if (empty($process_info)) { + throw new Exception('审批流程不存在'); + } + + // 检查是否当前审批人 + if ($process_info['current_approver_id'] != $approver_id) { + throw new Exception('您不是当前审批人'); + } + + // 检查流程状态 + if ($process_info['approval_status'] != SchoolApprovalProcess::STATUS_PENDING) { + throw new Exception('该审批流程已完成'); + } + + // 获取当前审批节点 + $current_participant = (new SchoolApprovalParticipants()) + ->where([ + 'process_id' => $process_id, + 'participant_id' => $approver_id, + 'status' => SchoolApprovalParticipants::STATUS_PENDING + ]) + ->find(); + + if (empty($current_participant)) { + throw new Exception('审批节点信息错误'); + } + + // 更新当前审批人状态 + (new SchoolApprovalParticipants())->where(['id' => $current_participant['id']]) + ->update([ + 'status' => $status, + 'remarks' => $remarks + ]); + + // 如果拒绝,直接更新整个流程状态为拒绝 + if ($status == SchoolApprovalParticipants::STATUS_REJECTED) { + (new SchoolApprovalProcess())->where(['id' => $process_id]) + ->update([ + 'approval_status' => SchoolApprovalProcess::STATUS_REJECTED, + 'approval_time' => time(), + 'remarks' => $remarks + ]); + + Db::commit(); + return true; + } + + // 检查当前节点是否需要会签 + $same_sequence_participants = (new SchoolApprovalParticipants()) + ->where([ + 'process_id' => $process_id, + 'sequence' => $current_participant['sequence'], + 'status' => SchoolApprovalParticipants::STATUS_PENDING + ]) + ->select(); + + // 如果是会签且还有其他人未审批,则等待 + if ($current_participant['sign_type'] == SchoolApprovalParticipants::SIGN_TYPE_AND && !$same_sequence_participants->isEmpty()) { + // 不做任何处理,等待其他人审批 + Db::commit(); + return true; + } + + // 获取下一个审批节点 + $next_participant = (new SchoolApprovalParticipants()) + ->where([ + 'process_id' => $process_id, + 'status' => SchoolApprovalParticipants::STATUS_PENDING + ]) + ->order('sequence', 'asc') + ->find(); + + if (empty($next_participant)) { + // 没有下一个审批人,流程结束,标记为已通过 + (new SchoolApprovalProcess())->where(['id' => $process_id]) + ->update([ + 'approval_status' => SchoolApprovalProcess::STATUS_APPROVED, + 'approval_time' => time() + ]); + } else { + // 更新当前审批人为下一个审批人 + (new SchoolApprovalProcess())->where(['id' => $process_id]) + ->update(['current_approver_id' => $next_participant['participant_id']]); + } + + Db::commit(); + return true; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } + + /** + * 撤销审批流程 + * @param int $process_id 流程ID + * @param int $applicant_id 申请人ID + * @return bool + * @throws \Exception + */ + public function cancel(int $process_id, int $applicant_id): bool + { + Db::startTrans(); + try { + // 获取审批流程 + $process_info = (new SchoolApprovalProcess())->where(['id' => $process_id])->find(); + if (empty($process_info)) { + throw new Exception('审批流程不存在'); + } + + // 检查是否申请人 + if ($process_info['applicant_id'] != $applicant_id) { + throw new Exception('您不是该流程的申请人'); + } + + // 检查流程状态 + if ($process_info['approval_status'] != SchoolApprovalProcess::STATUS_PENDING) { + throw new Exception('该审批流程已完成,无法撤销'); + } + + // 更新流程状态为已拒绝(撤销) + (new SchoolApprovalProcess())->where(['id' => $process_id]) + ->update([ + 'approval_status' => SchoolApprovalProcess::STATUS_REJECTED, + 'approval_time' => time(), + 'remarks' => '申请人撤销' + ]); + + // 更新所有待审批节点为已拒绝 + (new SchoolApprovalParticipants())->where([ + 'process_id' => $process_id, + 'status' => SchoolApprovalParticipants::STATUS_PENDING + ])->update([ + 'status' => SchoolApprovalParticipants::STATUS_REJECTED, + 'remarks' => '申请人撤销' + ]); + + Db::commit(); + return true; + } catch (\Exception $e) { + Db::rollback(); + throw new Exception($e->getMessage()); + } + } +} \ No newline at end of file