智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1136 lines
39 KiB

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
namespace app\service\api\student;
use think\facade\Db;
use core\base\BaseService;
use core\exception\CommonException;
use PhpOffice\PhpWord\TemplateProcessor;
use app\service\core\contract_sign\ContractSign;
/**
* 学员合同管理服务类
*/
class ContractService extends BaseService
{
/**
* 获取学员合同列表
* @param array $params
* @return array
*/
public function getContractList($params)
{
$studentId = $params['student_id'];
$status = $params['status'] ?? '';
$page = $params['page'] ?? 1;
$limit = $params['limit'] ?? 10;
// 构建查询条件
$where = [
['cs.student_id', '=', $studentId],
['cs.deleted_at', '=', 0],
['c.deleted_at', '=', 0]
];
// 状态筛选
if ($status !== '') {
$where[] = ['cs.status', '=', $status];
}
// 分页查询合同签署记录
$query = Db::table('school_contract_sign cs')
->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'];
// 对于已签署成功的合同(status=3),进行占位符替换
if ($contractSign['status'] == 3 && !empty($contractSign['terms'])) {
$contractSign['terms'] = $this->replacePlaceholdersInTerms(
$contractSign['terms'],
$contractId,
$contractSign['sign_id'],
$studentId
);
}
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)
->field('id, contract_name, contract_type, contract_template, contract_content, placeholders')
->find();
if (!$contract) {
throw new CommonException('合同模板不存在');
}
// 获取所有字段配置
$formFields = Db::table('school_document_data_source_config')
->where('contract_id', $contractId)
->field('id, placeholder, field_type, data_type, is_required, default_value, table_name, field_name, system_function, sign_party')
->order('id ASC')
->select()
->toArray();
// 格式化表单字段
$fields = [];
foreach ($formFields as $field) {
// 根据数据类型预填充默认值
$defaultValue = '';
switch ($field['data_type']) {
case 'database':
$defaultValue = $this->getDataFromDatabase($field['table_name'], $field['field_name'], $studentId);
break;
case 'system':
$defaultValue = $this->getSystemValue($field['system_function']);
break;
case 'user_input':
default:
$defaultValue = $field['default_value'] ?: '';
break;
}
$fields[] = [
'id' => $field['id'],
'name' => $field['placeholder'], // 使用placeholder作为字段名称
'placeholder' => $field['placeholder'],
'field_type' => $field['field_type'],
'data_type' => $field['data_type'],
'is_required' => (bool)$field['is_required'],
'default_value' => $defaultValue,
'sign_party' => $field['sign_party']
];
}
return [
'contract_id' => $contractId,
'contract_name' => $contract['contract_name'],
'contract_type' => $contract['contract_type'],
'contract_content' => $contract['contract_content'] ?: '',
'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('当前合同状态不允许签署');
}
// 从form_data中提取签名图片路径
$extractedSignatures = $this->extractSignatureImages($formData);
// 上传并处理签名图片
$processedSignatures = $this->uploadSignatureImages($extractedSignatures);
// 更新form_data中的签名路径为服务器路径
$formData = $this->updateFormDataWithUploadedPaths($formData, $processedSignatures);
// 验证必填字段
$this->validateFormData($contractId, $formData);
// 开始事务
Db::startTrans();
try {
// 生成签署后的合同文档
$generatedFile = null;
$mainSignature = $processedSignatures['{{学员签名}}'] ?? $signatureImage;
if ($mainSignature || !empty($processedSignatures)) {
$generatedFile = $this->generateSignedContract($contractId, $studentId, $formData, $mainSignature);
}
// 更新合同签署状态
$updateData = [
'status' => 3, // 直接设为已生效
'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 ($mainSignature) {
$updateData['signature_image'] = $mainSignature;
}
if ($generatedFile) {
$updateData['sign_file'] = $generatedFile;
}
$result = Db::table('school_contract_sign')
->where('id', $contractSign['id'])
->update($updateData);
if ($result === false) {
throw new CommonException('合同签署失败');
}
Db::commit();
return [
'sign_id' => $contractSign['id'],
'generated_file' => $generatedFile,
'sign_time' => $updateData['sign_time'],
'status' => 3,
'processed_signatures' => $processedSignatures
];
} 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) {
throw new CommonException('合同不存在');
}
// 优先返回已签署的文档
if ($contractSign['sign_file'] && $contractSign['status'] >= 2) {
// 检查已签署文档是否存在
$signedFilePath = public_path() . '/upload/' . $contractSign['sign_file'];
if (file_exists($signedFilePath)) {
return [
'file_url' => get_image_url($contractSign['sign_file']),
'file_name' => $contract['contract_name'] . '_已签署.docx',
'contract_name' => $contract['contract_name'],
'file_type' => 'signed', // 标识为已签署文档
'status' => $contractSign['status'],
'sign_time' => $contractSign['sign_time']
];
}
}
// 如果没有已签署文档或文件不存在,返回原始模板
if (!$contract['contract_template']) {
throw new CommonException('合同文件不存在');
}
return [
'file_url' => get_image_url($contract['contract_template']),
'file_name' => $contract['contract_name'] . '_模板.docx',
'contract_name' => $contract['contract_name'],
'file_type' => 'template', // 标识为模板文档
'status' => $contractSign['status']
];
}
/**
* 获取合同相关的课程信息
* @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 int $studentId
* @param array $formData
* @param string $signatureImage
* @return string
* @throws CommonException
*/
private function generateSignedContract($contractId, $studentId, $formData, $signatureImage)
{
try {
// 获取合同模板信息
$contract = Db::table('school_contract')
->where('id', $contractId)
->find();
if (!$contract || !$contract['contract_template']) {
throw new CommonException('合同模板不存在');
}
// 构建模板路径
$templatePath = public_path() . '/upload/' . $contract['contract_template'];
if (!file_exists($templatePath)) {
throw new CommonException('合同模板文件不存在');
}
// 生成输出文件名和路径
$outputFileName = 'signed_contract_' . $studentId . '_' . $contractId . '_' . date('YmdHis') . '.docx';
$outputRelPath = 'contracts/signed/' . date('Y/m/') . $outputFileName;
$outputFullPath = public_path() . '/upload/' . $outputRelPath;
// 确保目录存在
$outputDir = dirname($outputFullPath);
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
// 获取数据源配置并准备填充数据
$fillData = $this->prepareFillData($contractId, $studentId, $formData);
// 使用PhpWord处理模板
$templateProcessor = new TemplateProcessor($templatePath);
// 填充文本数据
foreach ($fillData as $placeholder => $value) {
$templateProcessor->setValue($placeholder, $value);
}
// 处理签名图片
if ($signatureImage && $this->hasSignaturePlaceholder($templateProcessor)) {
// 处理签名图片 - 支持base64和URL
$signImagePath = $this->processSignatureImage($signatureImage);
// 使用ContractSign服务插入签名
$contractSignService = new ContractSign();
$contractSignService->setSign($templatePath, $outputFullPath, $signImagePath, '学员签名');
// 清理临时文件
if (file_exists($signImagePath)) {
unlink($signImagePath);
}
} else {
// 没有签名时直接保存
$templateProcessor->saveAs($outputFullPath);
}
return $outputRelPath;
} catch (\Exception $e) {
throw new CommonException('生成签署合同失败:' . $e->getMessage());
}
}
/**
* 检查模板是否包含签名占位符
* @param TemplateProcessor $templateProcessor
* @return bool
*/
private function hasSignaturePlaceholder($templateProcessor)
{
// 这里可以检查模板是否包含签名占位符
// 简化处理,假设所有模板都支持签名
return true;
}
/**
* 处理签名图片
* @param string $signatureImage
* @return string
* @throws CommonException
*/
private function processSignatureImage($signatureImage)
{
$tempImagePath = public_path() . '/upload/temp_sign_' . date('YmdHis') . '_' . mt_rand(1000, 9999) . '.png';
if (strpos($signatureImage, 'data:image') === 0) {
// Base64图片
$imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $signatureImage));
if ($imageData === false) {
throw new CommonException('签名图片格式错误');
}
file_put_contents($tempImagePath, $imageData);
} elseif (filter_var($signatureImage, FILTER_VALIDATE_URL)) {
// URL图片
$imageContent = file_get_contents($signatureImage);
if ($imageContent === false) {
throw new CommonException('无法下载签名图片');
}
file_put_contents($tempImagePath, $imageContent);
} else {
// 本地路径
$localPath = public_path() . '/upload/' . ltrim($signatureImage, '/');
if (!file_exists($localPath)) {
throw new CommonException('签名图片文件不存在');
}
copy($localPath, $tempImagePath);
}
return $tempImagePath;
}
/**
* 准备填充数据
* @param int $contractId
* @param int $studentId
* @param array $formData
* @return array
*/
private function prepareFillData($contractId, $studentId, $formData)
{
$fillData = [];
// 获取数据源配置
$configs = Db::table('school_document_data_source_config')
->where('contract_id', $contractId)
->select()
->toArray();
foreach ($configs as $config) {
$placeholder = str_replace(['{{', '}}'], '', $config['placeholder']);
$value = '';
switch ($config['data_type']) {
case 'database':
$value = $this->getDataFromDatabase($config['table_name'], $config['field_name'], $studentId);
break;
case 'system':
$value = $this->getSystemValue($config['system_function']);
break;
case 'user_input':
default:
$value = $formData[$placeholder] ?? $config['default_value'] ?? '';
break;
}
$fillData[$placeholder] = $value;
}
return $fillData;
}
/**
* 从数据库获取数据
* @param string $tableName
* @param string $fieldName
* @param int $studentId
* @return string
*/
private function getDataFromDatabase($tableName, $fieldName, $studentId)
{
try {
if ($tableName === 'school_student') {
$data = Db::table($tableName)->where('id', $studentId)->value($fieldName);
} else {
// 其他表可能需要更复杂的关联查询
$data = Db::table($tableName)->where('student_id', $studentId)->value($fieldName);
}
return $data ?: '';
} catch (\Exception $e) {
return '';
}
}
/**
* 获取系统值
* @param string $systemFunction
* @return string
*/
private function getSystemValue($systemFunction)
{
switch ($systemFunction) {
case 'current_date':
return date('Y-m-d');
case 'current_time':
return date('H:i:s');
case 'current_datetime':
return date('Y-m-d H:i:s');
case 'current_year':
return date('Y');
case 'current_month':
return date('m');
case 'current_day':
return date('d');
case 'random_number':
return mt_rand(100000, 999999);
case 'contract_generate_time':
return date('Y-m-d H:i:s');
default:
return '';
}
}
/**
* 验证表单数据
* @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) {
$fieldName = str_replace(['{{', '}}'], '', $field);
if (!isset($formData[$fieldName]) || trim($formData[$fieldName]) === '') {
throw new CommonException($fieldName . ' 为必填项');
}
}
}
/**
* 从表单数据中提取签名图片路径
* @param array $formData
* @return array
*/
private function extractSignatureImages($formData)
{
$signatures = [];
// 常见的签名字段模式
$signaturePatterns = ['签名', '乙方', '甲方', '学员签名'];
foreach ($formData as $key => $value) {
// 检查是否为图片URL(临时路径或正式路径)
if (is_string($value) && (
strpos($value, 'http://tmp/') === 0 ||
strpos($value, 'https://tmp/') === 0 ||
preg_match('/\.(png|jpg|jpeg|gif)$/i', $value)
)) {
// 检查是否包含签名相关关键词
foreach ($signaturePatterns as $pattern) {
if (strpos($key, $pattern) !== false) {
$signatures[$key] = $value;
break;
}
}
}
}
return $signatures;
}
/**
* 上传签名图片到服务器
* @param array $signatures
* @return array
*/
private function uploadSignatureImages($signatures)
{
$processed = [];
foreach ($signatures as $key => $imagePath) {
try {
// 如果是临时路径,需要下载并保存
if (strpos($imagePath, 'http://tmp/') === 0 || strpos($imagePath, 'https://tmp/') === 0) {
// 微信小程序临时文件路径,需要通过API下载
// 这里简化处理,直接生成一个占位图片路径
$filename = 'signature_' . date('YmdHis') . '_' . mt_rand(1000, 9999) . '.png';
$relativePath = 'signatures/' . date('Y/m/') . $filename;
$fullPath = public_path() . '/upload/' . $relativePath;
// 确保目录存在
$dir = dirname($fullPath);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
// 尝试下载临时文件(实际环境中需要通过微信API)
// 这里模拟处理,生成一个占位文件
$placeholderContent = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
file_put_contents($fullPath, $placeholderContent);
$processed[$key] = $relativePath;
} else {
// 已经是正式路径,直接使用
$processed[$key] = $imagePath;
}
} catch (\Exception $e) {
// 签名图片处理失败,记录日志但不中断流程
error_log('签名图片上传失败: ' . $e->getMessage());
$processed[$key] = $imagePath; // 保持原路径
}
}
return $processed;
}
/**
* 更新表单数据中的签名路径
* @param array $formData
* @param array $processedSignatures
* @return array
*/
private function updateFormDataWithUploadedPaths($formData, $processedSignatures)
{
foreach ($processedSignatures as $key => $newPath) {
if (isset($formData[$key])) {
$formData[$key] = $newPath;
}
}
return $formData;
}
/**
* 在合同条款中替换占位符
* 使用 school_document_data_source_config 表中的 default_value 数据进行替换
*
* @param string $terms 合同条款内容
* @param int $contractId 合同模板ID
* @param int $contractSignId 合同签署记录ID
* @param int $studentId 学员ID
* @return string 替换后的合同条款
*/
private function replacePlaceholdersInTerms($terms, $contractId, $contractSignId, $studentId)
{
try {
if (empty($terms)) {
return $terms;
}
// 获取该合同的所有占位符配置
$configs = Db::table('school_document_data_source_config')
->where('contract_id', $contractId)
->where(function($query) use ($contractSignId) {
$query->where('contract_sign_id', $contractSignId)
->whereOr('contract_sign_id', 'IS', null);
})
->field('placeholder, data_type, default_value, table_name, field_name, system_function')
->select()
->toArray();
if (empty($configs)) {
return $terms;
}
// 获取学员信息(用于数据库类型查询)
$student = Db::table('school_student')
->where('id', $studentId)
->find();
// 遍历配置,逐个替换占位符
foreach ($configs as $config) {
$placeholder = $config['placeholder'];
$value = '';
// 优先使用 default_value
if (!empty($config['default_value'])) {
$value = $config['default_value'];
} else {
// 根据数据类型获取值
switch ($config['data_type']) {
case 'database':
$value = $this->getDatabaseFieldValueFromConfig($config, $student ?: []);
break;
case 'system':
$value = $this->getSystemFunctionValueFromConfig($config);
break;
case 'user_input':
default:
$value = $config['default_value'] ?: '';
break;
}
}
// 替换占位符(支持 {{placeholder}} 和 {placeholder} 格式)
$patterns = [
'{{' . $placeholder . '}}',
'{' . $placeholder . '}'
];
foreach ($patterns as $pattern) {
$terms = str_replace($pattern, $value, $terms);
}
}
return $terms;
} catch (\Exception $e) {
// 出现错误时返回原内容,记录日志
\think\facade\Log::error('合同条款占位符替换失败', [
'contract_id' => $contractId,
'contract_sign_id' => $contractSignId,
'student_id' => $studentId,
'error' => $e->getMessage()
]);
return $terms;
}
}
/**
* 从配置中获取数据库字段值
* @param array $config 字段配置
* @param array $student 学员信息
* @return string
*/
private function getDatabaseFieldValueFromConfig($config, $student)
{
try {
$tableName = $config['table_name'] ?? '';
$fieldName = $config['field_name'] ?? '';
if (empty($tableName) || empty($fieldName)) {
return '';
}
$value = '';
switch ($tableName) {
case 'school_student':
case 'students':
// 学员表:直接从学员信息获取
$value = $student[$fieldName] ?? '';
break;
case 'school_customer_resources':
// 用户表:通过学员的user_id关联查询
if (!empty($student['user_id'])) {
$value = Db::table('school_customer_resources')
->where('id', $student['user_id'])
->value($fieldName) ?: '';
}
break;
case 'school_order_table':
// 订单表:查询该学员最新的订单信息
$value = Db::table('school_order_table')
->where('student_id', $student['id'])
->order('created_at', 'desc')
->value($fieldName) ?: '';
break;
case 'school_personnel':
// 员工表:这里可能需要根据业务逻辑确定关联的员工
$value = '';
break;
default:
\think\facade\Log::warning('不支持的数据库表', [
'table_name' => $tableName,
'field_name' => $fieldName
]);
break;
}
// 格式化字段值
$value = $this->formatFieldValue($fieldName, $value);
return (string)$value;
} catch (\Exception $e) {
\think\facade\Log::error('从配置获取数据库字段值失败', [
'config' => $config,
'student_id' => $student['id'] ?? 0,
'error' => $e->getMessage()
]);
return '';
}
}
/**
* 从配置中获取系统函数值
* @param array $config 字段配置
* @return string
*/
private function getSystemFunctionValueFromConfig($config)
{
try {
$systemFunction = $config['system_function'] ?? '';
if (empty($systemFunction)) {
return '';
}
$value = '';
switch ($systemFunction) {
case 'current_date':
$value = date('Y-m-d');
break;
case 'current_time':
$value = date('H:i:s');
break;
case 'current_datetime':
$value = date('Y-m-d H:i:s');
break;
case 'current_year':
$value = date('Y');
break;
case 'current_month':
$value = date('m');
break;
case 'current_day':
$value = date('d');
break;
case 'random_number':
$value = mt_rand(100000, 999999);
break;
case 'contract_generate_time':
$value = date('Y-m-d H:i:s');
break;
default:
\think\facade\Log::warning('不支持的系统函数', [
'system_function' => $systemFunction
]);
break;
}
return (string)$value;
} catch (\Exception $e) {
\think\facade\Log::error('从配置获取系统函数值失败', [
'config' => $config,
'error' => $e->getMessage()
]);
return '';
}
}
/**
* 格式化字段值
* 对特定类型的字段值进行格式化处理
*
* @param string $fieldName 字段名
* @param mixed $value 原始值
* @return string 格式化后的值
*/
private function formatFieldValue($fieldName, $value)
{
if (empty($value)) {
return '';
}
// 根据字段名进行特殊处理
switch (true) {
case str_contains($fieldName, 'date') || str_contains($fieldName, '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($fieldName, 'amount') || str_contains($fieldName, 'price'):
// 金额字段格式化
return number_format((float)$value, 2);
case str_contains($fieldName, 'phone'):
// 手机号格式化(可以添加脱敏处理)
return (string)$value;
default:
// 默认返回字符串
return (string)$value;
}
return (string)$value;
}
}