11 changed files with 834 additions and 118 deletions
@ -0,0 +1,601 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
namespace app\service\api\apiService; |
||||
|
|
||||
|
use core\base\BaseApiService; |
||||
|
use think\facade\Db; |
||||
|
use think\facade\Log; |
||||
|
|
||||
|
/** |
||||
|
* 合同签署表单服务类 |
||||
|
* 负责处理学员端合同签署相关的业务逻辑 |
||||
|
* Class ContractSignFormService |
||||
|
* @package app\service\api\apiService |
||||
|
*/ |
||||
|
class ContractSignFormService extends BaseApiService |
||||
|
{ |
||||
|
public function __construct() |
||||
|
{ |
||||
|
parent::__construct(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取学员合同签署表单配置 |
||||
|
* 该方法为移动端提供合同签署表单的完整配置信息 |
||||
|
* 包括合同基本信息、占位符字段配置、预填充数据等 |
||||
|
* |
||||
|
* @param array $params 请求参数 |
||||
|
* - contract_id: 合同模板ID (必填) |
||||
|
* - student_id: 学员ID (必填) |
||||
|
* @return array 返回格式化的表单配置数据 |
||||
|
* @throws \Exception 当参数验证失败或数据获取异常时抛出异常 |
||||
|
*/ |
||||
|
public function getStudentContractSignForm(array $params) |
||||
|
{ |
||||
|
try { |
||||
|
// 1. 验证必要参数 |
||||
|
if (empty($params['contract_id']) || empty($params['student_id'])) { |
||||
|
throw new \Exception('缺少必要参数:contract_id 和 student_id'); |
||||
|
} |
||||
|
|
||||
|
$contract_id = (int)$params['contract_id']; |
||||
|
$student_id = (int)$params['student_id']; |
||||
|
|
||||
|
Log::info('开始获取学员合同签署表单', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id |
||||
|
]); |
||||
|
|
||||
|
// 2. 获取合同模板基本信息 |
||||
|
$contract = $this->getContractInfo($contract_id); |
||||
|
if (!$contract) { |
||||
|
throw new \Exception('合同模板不存在或已删除'); |
||||
|
} |
||||
|
|
||||
|
// 3. 获取学员基本信息 |
||||
|
$student = $this->getStudentInfo($student_id); |
||||
|
if (!$student) { |
||||
|
throw new \Exception('学员信息不存在'); |
||||
|
} |
||||
|
|
||||
|
// 4. 获取占位符配置信息 |
||||
|
$placeholder_config = $this->getPlaceholderConfig($contract_id); |
||||
|
|
||||
|
// 5. 处理占位符配置,生成表单字段 |
||||
|
$form_fields = $this->processFormFields($placeholder_config, $student); |
||||
|
|
||||
|
// 6. 组装返回数据 |
||||
|
$result = [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'contract_name' => $contract['contract_name'], |
||||
|
'contract_type' => $contract['contract_type'], |
||||
|
'contract_content' => $contract['contract_content'] ?? '', |
||||
|
'form_fields' => $form_fields, |
||||
|
'student_info' => [ |
||||
|
'id' => $student['id'], |
||||
|
'name' => $student['name'], |
||||
|
'phone' => $student['contact_phone'] ?? '', |
||||
|
'user_id' => $student['user_id'] |
||||
|
] |
||||
|
]; |
||||
|
|
||||
|
Log::info('学员合同签署表单获取成功', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id, |
||||
|
'form_fields_count' => count($form_fields) |
||||
|
]); |
||||
|
|
||||
|
return [ |
||||
|
'code' => 1, |
||||
|
'msg' => '获取成功', |
||||
|
'data' => $result |
||||
|
]; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取学员合同签署表单失败', [ |
||||
|
'contract_id' => $params['contract_id'] ?? 0, |
||||
|
'student_id' => $params['student_id'] ?? 0, |
||||
|
'error' => $e->getMessage(), |
||||
|
'trace' => $e->getTraceAsString() |
||||
|
]); |
||||
|
|
||||
|
return [ |
||||
|
'code' => 0, |
||||
|
'msg' => $e->getMessage(), |
||||
|
'data' => [] |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取合同模板基本信息 |
||||
|
* 从数据库中获取合同模板的基础信息 |
||||
|
* |
||||
|
* @param int $contract_id 合同模板ID |
||||
|
* @return array|null 合同信息数组,失败返回null |
||||
|
*/ |
||||
|
private function getContractInfo($contract_id) |
||||
|
{ |
||||
|
try { |
||||
|
$contract = Db::table('school_contract') |
||||
|
->where([ |
||||
|
['id', '=', $contract_id], |
||||
|
['contract_status', '=', 'active'], // 只获取启用状态的合同 |
||||
|
['deleted_at', '=', 0] |
||||
|
]) |
||||
|
->field([ |
||||
|
'id', 'contract_name', 'contract_type', 'contract_content', |
||||
|
'placeholder_config', 'created_at', 'updated_at' |
||||
|
]) |
||||
|
->find(); |
||||
|
|
||||
|
return $contract ? $contract : null; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取合同信息失败', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取学员基本信息 |
||||
|
* 从数据库中获取学员的详细信息,用于表单预填充 |
||||
|
* |
||||
|
* @param int $student_id 学员ID |
||||
|
* @return array|null 学员信息数组,失败返回null |
||||
|
*/ |
||||
|
private function getStudentInfo($student_id) |
||||
|
{ |
||||
|
try { |
||||
|
$student = Db::table('school_student') |
||||
|
->where([ |
||||
|
['id', '=', $student_id], |
||||
|
['status', '=', 1] // 只获取有效学员 |
||||
|
]) |
||||
|
->field([ |
||||
|
'id', 'name', 'gender', 'age', 'birthday', |
||||
|
'emergency_contact', 'contact_phone', 'member_label', |
||||
|
'user_id', 'campus_id', 'created_at' |
||||
|
]) |
||||
|
->find(); |
||||
|
|
||||
|
return $student ? $student : null; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取学员信息失败', [ |
||||
|
'student_id' => $student_id, |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取合同占位符配置 |
||||
|
* 从合同模板中解析占位符配置信息 |
||||
|
* 支持从 placeholder_config JSON字段获取配置 |
||||
|
* |
||||
|
* @param int $contract_id 合同模板ID |
||||
|
* @return array 占位符配置数组 |
||||
|
*/ |
||||
|
private function getPlaceholderConfig($contract_id) |
||||
|
{ |
||||
|
try { |
||||
|
// 从合同表获取占位符配置 |
||||
|
$placeholder_config_json = Db::table('school_contract') |
||||
|
->where('id', $contract_id) |
||||
|
->value('placeholder_config'); |
||||
|
|
||||
|
if (empty($placeholder_config_json)) { |
||||
|
Log::warning('合同占位符配置为空', ['contract_id' => $contract_id]); |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
$config = json_decode($placeholder_config_json, true); |
||||
|
if (!is_array($config)) { |
||||
|
Log::warning('合同占位符配置格式错误', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'config' => $placeholder_config_json |
||||
|
]); |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
Log::info('获取占位符配置成功', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'config_count' => count($config), |
||||
|
'raw_config_length' => strlen($placeholder_config_json), |
||||
|
'config_keys' => array_keys($config) |
||||
|
]); |
||||
|
|
||||
|
return $config; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取占位符配置失败', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return []; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理表单字段配置 |
||||
|
* 根据占位符配置生成移动端可用的表单字段数据 |
||||
|
* 对不同数据类型进行差异化处理,预填充相应的默认值 |
||||
|
* |
||||
|
* @param array $placeholder_config 占位符配置数组 |
||||
|
* @param array $student 学员信息 |
||||
|
* @return array 处理后的表单字段数组 |
||||
|
*/ |
||||
|
private function processFormFields($placeholder_config, $student) |
||||
|
{ |
||||
|
$form_fields = []; |
||||
|
|
||||
|
Log::info('开始处理表单字段配置', [ |
||||
|
'config_type' => gettype($placeholder_config), |
||||
|
'config_count' => is_array($placeholder_config) ? count($placeholder_config) : 0, |
||||
|
'config_keys' => is_array($placeholder_config) ? array_keys($placeholder_config) : [] |
||||
|
]); |
||||
|
|
||||
|
// 检查配置数据格式 |
||||
|
if (!is_array($placeholder_config) || empty($placeholder_config)) { |
||||
|
Log::warning('占位符配置为空或格式错误'); |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
// 处理JSON对象格式的配置:{"字段名": {配置}} |
||||
|
foreach ($placeholder_config as $placeholder_name => $config) { |
||||
|
// 确保$config是数组 |
||||
|
if (!is_array($config)) { |
||||
|
Log::warning('跳过无效配置项', ['placeholder' => $placeholder_name, 'config' => $config]); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// 基础字段信息 - 使用键名作为占位符名称 |
||||
|
$field = [ |
||||
|
'name' => $placeholder_name, |
||||
|
'placeholder' => $placeholder_name, |
||||
|
'data_type' => $config['data_type'] ?? 'user_input', |
||||
|
'field_type' => $config['field_type'] ?? 'text', |
||||
|
'is_required' => (int)($config['is_required'] ?? 0), |
||||
|
'default_value' => '', |
||||
|
'validation_rule' => $config['validation_rule'] ?? '', |
||||
|
'sign_party' => $config['sign_party'] ?? '', |
||||
|
]; |
||||
|
|
||||
|
// 根据数据类型处理默认值 |
||||
|
switch ($config['data_type'] ?? 'user_input') { |
||||
|
case 'database': |
||||
|
// 数据库类型:从相关表获取数据 |
||||
|
$field['default_value'] = $this->getDatabaseFieldValue($config, $student); |
||||
|
break; |
||||
|
|
||||
|
case 'system': |
||||
|
// 系统函数类型:调用系统函数获取值 |
||||
|
$field['default_value'] = $this->getSystemFunctionValue($config); |
||||
|
break; |
||||
|
|
||||
|
case 'user_input': |
||||
|
// 用户输入类型:使用配置的默认值 |
||||
|
$field['default_value'] = $config['default_value'] ?? ''; |
||||
|
break; |
||||
|
|
||||
|
case 'signature': |
||||
|
// 电子签名类型:无默认值,需要用户手写签名 |
||||
|
$field['signature_type'] = $config['signature_type'] ?? 'handwrite'; |
||||
|
$field['default_value'] = ''; |
||||
|
break; |
||||
|
|
||||
|
case 'sign_img': |
||||
|
// 签名图片类型:无默认值,需要用户上传或选择 |
||||
|
$field['sign_image_source'] = $config['sign_image_source'] ?? 'upload'; |
||||
|
$field['default_value'] = ''; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
$field['default_value'] = $config['default_value'] ?? ''; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
$form_fields[] = $field; |
||||
|
} |
||||
|
|
||||
|
Log::info('表单字段处理完成', [ |
||||
|
'total_fields' => count($form_fields), |
||||
|
'database_fields' => count(array_filter($form_fields, fn($f) => $f['data_type'] === 'database')), |
||||
|
'system_fields' => count(array_filter($form_fields, fn($f) => $f['data_type'] === 'system')), |
||||
|
'user_input_fields' => count(array_filter($form_fields, fn($f) => $f['data_type'] === 'user_input')), |
||||
|
'signature_fields' => count(array_filter($form_fields, fn($f) => $f['data_type'] === 'signature')), |
||||
|
'sign_img_fields' => count(array_filter($form_fields, fn($f) => $f['data_type'] === 'sign_img')), |
||||
|
'field_names' => array_column($form_fields, 'name') |
||||
|
]); |
||||
|
|
||||
|
return $form_fields; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取数据库字段值 |
||||
|
* 根据配置的表名和字段名从数据库获取对应的值 |
||||
|
* 支持学员表、订单表、用户表、员工表等多种数据源 |
||||
|
* |
||||
|
* @param array $config 字段配置 |
||||
|
* @param array $student 学员信息(用于关联查询) |
||||
|
* @return string 字段值 |
||||
|
*/ |
||||
|
private function getDatabaseFieldValue($config, $student) |
||||
|
{ |
||||
|
try { |
||||
|
$table_name = $config['table_name'] ?? ''; |
||||
|
$field_name = $config['field_name'] ?? ''; |
||||
|
|
||||
|
if (empty($table_name) || empty($field_name)) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
$value = ''; |
||||
|
|
||||
|
switch ($table_name) { |
||||
|
case 'school_student': |
||||
|
// 学员表:直接从学员信息获取 |
||||
|
$value = $student[$field_name] ?? ''; |
||||
|
break; |
||||
|
|
||||
|
case 'school_customer_resources': |
||||
|
// 用户表:通过学员的user_id关联查询 |
||||
|
if (!empty($student['user_id'])) { |
||||
|
$value = Db::table('school_customer_resources') |
||||
|
->where('id', $student['user_id']) |
||||
|
->value($field_name) ?? ''; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'school_order_table': |
||||
|
// 订单表:查询该学员最新的订单信息 |
||||
|
$value = Db::table('school_order_table') |
||||
|
->where('student_id', $student['id']) |
||||
|
->order('created_at', 'desc') |
||||
|
->value($field_name) ?? ''; |
||||
|
break; |
||||
|
|
||||
|
case 'school_personnel': |
||||
|
// 员工表:这里可能需要根据业务逻辑确定关联的员工 |
||||
|
// 暂时返回空值,具体业务逻辑需要根据实际需求调整 |
||||
|
$value = ''; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
Log::warning('不支持的数据库表', [ |
||||
|
'table_name' => $table_name, |
||||
|
'field_name' => $field_name |
||||
|
]); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// 对特殊字段进行格式化处理 |
||||
|
$value = $this->formatFieldValue($field_name, $value); |
||||
|
|
||||
|
return (string)$value; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取数据库字段值失败', [ |
||||
|
'config' => $config, |
||||
|
'student_id' => $student['id'], |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取系统函数值 |
||||
|
* 调用预定义的系统函数获取动态值 |
||||
|
* 支持日期时间、业务信息、系统信息等多种函数 |
||||
|
* |
||||
|
* @param array $config 字段配置 |
||||
|
* @return string 函数返回值 |
||||
|
*/ |
||||
|
private function getSystemFunctionValue($config) |
||||
|
{ |
||||
|
try { |
||||
|
$system_function = $config['system_function'] ?? ''; |
||||
|
|
||||
|
if (empty($system_function)) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
// 检查函数是否存在 |
||||
|
if (!function_exists($system_function)) { |
||||
|
Log::warning('系统函数不存在', [ |
||||
|
'function_name' => $system_function |
||||
|
]); |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
// 调用系统函数 |
||||
|
$value = call_user_func($system_function); |
||||
|
|
||||
|
Log::info('系统函数调用成功', [ |
||||
|
'function_name' => $system_function, |
||||
|
'result' => $value |
||||
|
]); |
||||
|
|
||||
|
return (string)$value; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('获取系统函数值失败', [ |
||||
|
'config' => $config, |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 格式化字段值 |
||||
|
* 对特定类型的字段值进行格式化处理 |
||||
|
* 例如日期格式化、金额格式化等 |
||||
|
* |
||||
|
* @param string $field_name 字段名 |
||||
|
* @param mixed $value 原始值 |
||||
|
* @return string 格式化后的值 |
||||
|
*/ |
||||
|
private function formatFieldValue($field_name, $value) |
||||
|
{ |
||||
|
if (empty($value)) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
// 根据字段名进行特殊处理 |
||||
|
switch (true) { |
||||
|
case str_contains($field_name, 'date') || str_contains($field_name, 'time'): |
||||
|
// 日期时间字段格式化 |
||||
|
if (is_numeric($value)) { |
||||
|
return date('Y-m-d', $value); |
||||
|
} elseif (strtotime($value)) { |
||||
|
return date('Y-m-d', strtotime($value)); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case str_contains($field_name, 'amount') || str_contains($field_name, 'price'): |
||||
|
// 金额字段格式化 |
||||
|
return number_format((float)$value, 2); |
||||
|
|
||||
|
case str_contains($field_name, 'phone'): |
||||
|
// 手机号格式化(可以添加脱敏处理) |
||||
|
return (string)$value; |
||||
|
|
||||
|
default: |
||||
|
// 默认返回字符串 |
||||
|
return (string)$value; |
||||
|
} |
||||
|
|
||||
|
return (string)$value; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交学员合同签署数据 |
||||
|
* 处理移动端提交的合同签署表单数据 |
||||
|
* 验证数据完整性并保存到数据库 |
||||
|
* |
||||
|
* @param array $params 提交参数 |
||||
|
* - contract_id: 合同模板ID |
||||
|
* - student_id: 学员ID |
||||
|
* - form_data: 表单数据数组 |
||||
|
* @return array 提交结果 |
||||
|
*/ |
||||
|
public function submitStudentContractSign(array $params) |
||||
|
{ |
||||
|
try { |
||||
|
// 验证必要参数 |
||||
|
if (empty($params['contract_id']) || empty($params['student_id']) || empty($params['form_data'])) { |
||||
|
throw new \Exception('缺少必要参数'); |
||||
|
} |
||||
|
|
||||
|
$contract_id = (int)$params['contract_id']; |
||||
|
$student_id = (int)$params['student_id']; |
||||
|
$form_data = $params['form_data']; |
||||
|
|
||||
|
Log::info('开始处理学员合同签署提交', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id, |
||||
|
'form_data_keys' => array_keys($form_data) |
||||
|
]); |
||||
|
|
||||
|
// 验证合同和学员是否存在 |
||||
|
$contract = $this->getContractInfo($contract_id); |
||||
|
if (!$contract) { |
||||
|
throw new \Exception('合同模板不存在'); |
||||
|
} |
||||
|
|
||||
|
$student = $this->getStudentInfo($student_id); |
||||
|
if (!$student) { |
||||
|
throw new \Exception('学员信息不存在'); |
||||
|
} |
||||
|
|
||||
|
// 保存签署记录 |
||||
|
$sign_id = $this->saveContractSignRecord($contract_id, $student_id, $form_data); |
||||
|
|
||||
|
if ($sign_id) { |
||||
|
Log::info('学员合同签署成功', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id, |
||||
|
'sign_id' => $sign_id |
||||
|
]); |
||||
|
|
||||
|
return [ |
||||
|
'code' => 1, |
||||
|
'msg' => '合同签署成功', |
||||
|
'data' => ['sign_id' => $sign_id] |
||||
|
]; |
||||
|
} else { |
||||
|
throw new \Exception('保存签署记录失败'); |
||||
|
} |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('学员合同签署失败', [ |
||||
|
'params' => $params, |
||||
|
'error' => $e->getMessage(), |
||||
|
'trace' => $e->getTraceAsString() |
||||
|
]); |
||||
|
|
||||
|
return [ |
||||
|
'code' => 0, |
||||
|
'msg' => $e->getMessage(), |
||||
|
'data' => [] |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 保存合同签署记录 |
||||
|
* 将签署数据保存到数据库中 |
||||
|
* |
||||
|
* @param int $contract_id 合同ID |
||||
|
* @param int $student_id 学员ID |
||||
|
* @param array $form_data 表单数据 |
||||
|
* @return int|false 签署记录ID,失败返回false |
||||
|
*/ |
||||
|
private function saveContractSignRecord($contract_id, $student_id, $form_data) |
||||
|
{ |
||||
|
try { |
||||
|
$now = date('Y-m-d H:i:s'); |
||||
|
|
||||
|
// 准备签署记录数据 |
||||
|
$sign_data = [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id, |
||||
|
'form_data' => json_encode($form_data, JSON_UNESCAPED_UNICODE), |
||||
|
'sign_status' => 1, // 已签署 |
||||
|
'sign_time' => $now, |
||||
|
'created_at' => $now, |
||||
|
'updated_at' => $now, |
||||
|
'deleted_at' => 0 |
||||
|
]; |
||||
|
|
||||
|
// 插入签署记录 |
||||
|
$sign_id = Db::table('school_contract_sign')->insertGetId($sign_data); |
||||
|
|
||||
|
return $sign_id; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
Log::error('保存合同签署记录失败', [ |
||||
|
'contract_id' => $contract_id, |
||||
|
'student_id' => $student_id, |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue