leftJoin('school_contract c', 'cs.contract_id = c.id') ->where($where) ->field(' cs.id as sign_id, cs.contract_id, cs.status, cs.sign_time, cs.created_at, c.contract_name, c.contract_type, c.remarks, c.contract_template ') ->order('cs.created_at desc'); $total = $query->count(); $list = $query->page($page, $limit)->select()->toArray(); // 处理每个合同的详细信息 foreach ($list as &$contract) { // 状态文本映射 $contract['status_text'] = $this->getStatusText($contract['status']); // 获取合同相关的课程信息 $courseInfo = $this->getContractCourseInfo($contract['contract_id'], $studentId); $contract = array_merge($contract, $courseInfo); // 格式化日期 $contract['sign_date'] = $contract['sign_time'] ? date('Y-m-d', strtotime($contract['sign_time'])) : null; $contract['create_date'] = date('Y-m-d', strtotime($contract['created_at'])); // 文件路径处理 $contract['contract_file_url'] = $contract['contract_template'] ? get_image_url($contract['contract_template']) : ''; // 计算课时使用进度 if ($contract['total_hours'] > 0) { $contract['progress_percent'] = round(($contract['used_hours'] / $contract['total_hours']) * 100, 1); } else { $contract['progress_percent'] = 0; } // 判断是否可以续约(生效状态且课时即将用完) $contract['can_renew'] = $contract['status'] == 3 && $contract['remaining_hours'] <= 5; } // 统计数据 $stats = $this->getContractStats($studentId); return [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit, 'has_more' => $total > $page * $limit, 'stats' => $stats ]; } /** * 获取合同详情 * @param int $contractId * @param int $studentId * @return array */ public function getContractDetail($contractId, $studentId) { // 查询合同签署记录 $contractSign = Db::table('school_contract_sign cs') ->leftJoin('school_contract c', 'cs.contract_id = c.id') ->where([ ['cs.contract_id', '=', $contractId], ['cs.student_id', '=', $studentId], ['cs.deleted_at', '=', 0] ]) ->field(' cs.id as sign_id, cs.contract_id, cs.status, cs.sign_time, cs.created_at, cs.fill_data, c.contract_name, c.contract_type, c.remarks, c.contract_template, c.contract_content, c.placeholders ') ->find(); if (!$contractSign) { throw new CommonException('合同不存在或无权限访问'); } // 获取课程信息 $courseInfo = $this->getContractCourseInfo($contractId, $studentId); $contractSign = array_merge($contractSign, $courseInfo); // 状态文本 $contractSign['status_text'] = $this->getStatusText($contractSign['status']); // 格式化日期 $contractSign['sign_date'] = $contractSign['sign_time'] ? date('Y-m-d H:i:s', strtotime($contractSign['sign_time'])) : null; $contractSign['create_date'] = date('Y-m-d H:i:s', strtotime($contractSign['created_at'])); // 文件路径 $contractSign['contract_file_url'] = $contractSign['contract_template'] ? get_image_url($contractSign['contract_template']) : ''; // 解析填写的数据 $contractSign['form_data'] = []; if ($contractSign['fill_data']) { $contractSign['form_data'] = json_decode($contractSign['fill_data'], true) ?: []; } // 合同条款(如果有内容的话) $contractSign['terms'] = $contractSign['contract_content'] ?: $contractSign['remarks']; return $contractSign; } /** * 获取合同签署表单配置 * @param int $contractId * @param int $studentId * @return array */ public function getSignForm($contractId, $studentId) { // 验证合同是否存在且用户有权限 $contractSign = Db::table('school_contract_sign') ->where([ ['contract_id', '=', $contractId], ['student_id', '=', $studentId], ['deleted_at', '=', 0] ]) ->find(); if (!$contractSign) { throw new CommonException('合同不存在或无权限访问'); } // 检查合同状态 if ($contractSign['status'] != 1) { throw new CommonException('当前合同状态不允许签署'); } // 获取合同基本信息 $contract = Db::table('school_contract') ->where('id', $contractId) ->find(); if (!$contract) { throw new CommonException('合同模板不存在'); } // 获取需要填写的字段配置 $formFields = Db::table('school_document_data_source_config') ->where([ ['contract_id', '=', $contractId], ['data_type', '=', 'manual'] ]) ->field('placeholder, field_type, is_required, default_value') ->select() ->toArray(); // 格式化表单字段 $fields = []; foreach ($formFields as $field) { $fields[] = [ 'name' => $field['placeholder'], 'type' => $field['field_type'], 'required' => (bool)$field['is_required'], 'default_value' => $field['default_value'] ?: '', 'placeholder' => '请输入' . $field['placeholder'] ]; } return [ 'contract_id' => $contractId, 'contract_name' => $contract['contract_name'], 'contract_type' => $contract['contract_type'], 'form_fields' => $fields, 'contract_template_url' => $contract['contract_template'] ? get_image_url($contract['contract_template']) : '' ]; } /** * 提交合同签署 * @param array $data * @return bool */ public function signContract($data) { $contractId = $data['contract_id']; $studentId = $data['student_id']; $formData = $data['form_data']; $signatureImage = $data['signature_image'] ?? ''; // 验证合同签署记录 $contractSign = Db::table('school_contract_sign') ->where([ ['contract_id', '=', $contractId], ['student_id', '=', $studentId], ['deleted_at', '=', 0] ]) ->find(); if (!$contractSign) { throw new CommonException('合同不存在或无权限访问'); } if ($contractSign['status'] != 1) { throw new CommonException('当前合同状态不允许签署'); } // 验证必填字段 $this->validateFormData($contractId, $formData); // 开始事务 Db::startTrans(); try { // 更新合同签署状态 $updateData = [ 'status' => 2, // 已签署 'sign_time' => date('Y-m-d H:i:s'), 'fill_data' => json_encode($formData, JSON_UNESCAPED_UNICODE), 'updated_at' => date('Y-m-d H:i:s') ]; if ($signatureImage) { $updateData['signature_image'] = $signatureImage; } $result = Db::table('school_contract_sign') ->where('id', $contractSign['id']) ->update($updateData); if ($result === false) { throw new CommonException('合同签署失败'); } Db::commit(); return true; } catch (\Exception $e) { Db::rollback(); throw new CommonException('合同签署失败:' . $e->getMessage()); } } /** * 下载合同文件 * @param int $contractId * @param int $studentId * @return array */ public function downloadContract($contractId, $studentId) { // 验证权限 $contractSign = Db::table('school_contract_sign') ->where([ ['contract_id', '=', $contractId], ['student_id', '=', $studentId], ['deleted_at', '=', 0] ]) ->find(); if (!$contractSign) { throw new CommonException('合同不存在或无权限访问'); } // 获取合同文件 $contract = Db::table('school_contract') ->where('id', $contractId) ->find(); if (!$contract || !$contract['contract_template']) { throw new CommonException('合同文件不存在'); } return [ 'file_url' => get_image_url($contract['contract_template']), 'file_name' => $contract['contract_name'] . '.pdf', 'contract_name' => $contract['contract_name'] ]; } /** * 获取合同相关的课程信息 * @param int $contractId * @param int $studentId * @return array */ private function getContractCourseInfo($contractId, $studentId) { // 通过订单表获取课程信息 $orderInfo = Db::table('school_order_table ot') ->leftJoin('school_course c', 'ot.course_id = c.id') ->where([ // 这里需要根据实际业务逻辑调整关联条件 ['ot.student_id', '=', $studentId] ]) ->field(' c.course_name, c.course_type, ot.order_amount as total_amount ') ->find(); // 从课程表获取课时信息 $courseStats = Db::table('school_student_courses') ->where('student_id', $studentId) ->field(' SUM(total_hours + gift_hours) as total_hours, SUM(use_total_hours + use_gift_hours) as used_hours, SUM(total_hours + gift_hours - use_total_hours - use_gift_hours) as remaining_hours ') ->find(); return [ 'course_type' => $orderInfo['course_name'] ?? '未知课程', 'total_amount' => $orderInfo['total_amount'] ?? '0.00', 'total_hours' => (int)($courseStats['total_hours'] ?? 0), 'used_hours' => (int)($courseStats['used_hours'] ?? 0), 'remaining_hours' => (int)($courseStats['remaining_hours'] ?? 0) ]; } /** * 获取合同统计数据 * @param int $studentId * @return array */ private function getContractStats($studentId) { // 统计各状态合同数量 $statusCounts = Db::table('school_contract_sign') ->where([ ['student_id', '=', $studentId], ['deleted_at', '=', 0] ]) ->field('status, COUNT(*) as count') ->group('status') ->select() ->toArray(); $stats = [ 'total_contracts' => 0, 'active_contracts' => 0, // 已生效 'pending_contracts' => 0, // 未签署 'signed_contracts' => 0, // 已签署 'expired_contracts' => 0, // 已失效 ]; foreach ($statusCounts as $item) { $stats['total_contracts'] += $item['count']; switch ($item['status']) { case 1: $stats['pending_contracts'] = $item['count']; break; case 2: $stats['signed_contracts'] = $item['count']; break; case 3: $stats['active_contracts'] = $item['count']; break; case 4: $stats['expired_contracts'] = $item['count']; break; } } // 获取剩余总课时 $courseStats = Db::table('school_student_courses') ->where('student_id', $studentId) ->field('SUM(total_hours + gift_hours - use_total_hours - use_gift_hours) as remaining_hours') ->find(); $stats['remaining_hours'] = (int)($courseStats['remaining_hours'] ?? 0); return $stats; } /** * 获取状态文本 * @param int $status * @return string */ private function getStatusText($status) { $statusMap = [ 1 => '未签署', 2 => '已签署', 3 => '已生效', 4 => '已失效' ]; return $statusMap[$status] ?? '未知状态'; } /** * 获取学员基本信息 * @param int $studentId * @return array */ public function getStudentInfo($studentId) { $student = Db::table('school_student') ->where('id', $studentId) ->field('id, name, gender, age, headimg') ->find(); if (!$student) { throw new CommonException('学员不存在'); } return [ 'id' => $student['id'], 'name' => $student['name'], 'gender' => $student['gender'], 'age' => $student['age'], 'avatar' => $student['headimg'] ? get_image_url($student['headimg']) : '' ]; } /** * 验证表单数据 * @param int $contractId * @param array $formData * @throws CommonException */ private function validateFormData($contractId, $formData) { // 获取必填字段配置 $requiredFields = Db::table('school_document_data_source_config') ->where([ ['contract_id', '=', $contractId], ['data_type', '=', 'manual'], ['is_required', '=', 1] ]) ->column('placeholder'); // 检查必填字段 foreach ($requiredFields as $field) { if (!isset($formData[$field]) || trim($formData[$field]) === '') { throw new CommonException($field . ' 为必填项'); } } } }