Browse Source

修改 bug

master
王泽彦 7 months ago
parent
commit
b524102345
  1. 14
      niucloud/app/api/controller/apiController/Contract.php
  2. 27
      niucloud/app/common.php
  3. 719
      niucloud/app/service/admin/document/DocumentTemplateService.php
  4. 36
      niucloud/app/service/api/apiService/ContractSignFormService.php
  5. 4
      uniapp/common/config.js

14
niucloud/app/api/controller/apiController/Contract.php

@ -488,15 +488,23 @@ class Contract extends BaseApiService
$fillData[$placeholder] = $defaultValue;
}
// 调用DocumentTemplateService生成Word文档
// 调试信息:记录填充数据
Log::info('准备的填充数据', [
'contract_sign_id' => $contract_sign_id,
'fill_data_count' => count($fillData),
'fill_data' => $fillData
]);
// 调用DocumentTemplateService的新XML字符串方法生成Word文档
$documentService = new \app\service\admin\document\DocumentTemplateService();
$generateData = [
'template_id' => $contractSign['contract_id'],
'fill_data' => $fillData,
'output_filename' => $contract['contract_name'] . '_' . $contractSign['student_id'] . '_' . date('YmdHis') . '.docx'
'output_filename' => $contract['contract_name'] . '_' . $contractSign['student_id'] . '_' . date('YmdHis') . '.docx',
'use_direct_values' => true // 直接使用已处理的值,不进行二次处理
];
$result = $documentService->generateDocument($generateData);
$result = $documentService->generateDocumentByXmlString($generateData);
// 更新school_contract_sign表的sign_file字段和status字段
$updateResult = Db::table('school_contract_sign')

27
niucloud/app/common.php

@ -1672,6 +1672,33 @@ function get_current_week()
return date('W');
}
/**
* 获取当前年份(别名函数,兼容配置中的函数名)
* @return string
*/
function current_year()
{
return get_current_year();
}
/**
* 获取当前月份(别名函数,兼容配置中的函数名)
* @return string
*/
function current_month()
{
return get_current_month();
}
/**
* 获取当前日(别名函数,兼容配置中的函数名)
* @return string
*/
function current_day()
{
return get_current_day();
}
/**
* 获取当前季度
* @return string

719
niucloud/app/service/admin/document/DocumentTemplateService.php

@ -652,7 +652,19 @@ class DocumentTemplateService extends BaseAdminService
// 准备填充数据
$placeholderConfig = json_decode($template['placeholder_config'], true);
$fillValues = $this->prepareFillData($placeholderConfig, $data['fill_data']);
// 检查是否传递了 use_direct_values 参数,如果是则直接使用传递的值
if (!empty($data['use_direct_values']) && $data['use_direct_values'] === true) {
// 直接使用传递的 fill_data 作为填充值,不进行二次处理
$fillValues = $data['fill_data'];
Log::info('使用直接填充模式', [
'template_id' => $data['template_id'],
'fill_data_count' => count($fillValues),
'fill_data_keys' => array_keys($fillValues)
]);
} else {
// 使用原有的配置处理模式
$fillValues = $this->prepareFillData($placeholderConfig, $data['fill_data']);
}
// 生成文档
$templatePath = public_path() . '/upload/' . $template['contract_template'];
@ -695,12 +707,14 @@ class DocumentTemplateService extends BaseAdminService
throw new \Exception('无法创建临时文档存储目录,请检查系统权限');
}
// 预处理:修复被格式化分割的占位符
$fixedTemplatePath = $this->fixBrokenPlaceholders($templatePath);
// 使用 PhpWord 模板处理器
$templateProcessor = new TemplateProcessor($templatePath);
$templateProcessor = new TemplateProcessor($fixedTemplatePath);
foreach ($fillValues as $placeholder => $value) {
$templateProcessor->setValue($placeholder, $value);
}
// 智能处理占位符,根据类型使用不同的处理方法
$this->processPlaceholders($templateProcessor, $fillValues, $placeholderConfig);
$templateProcessor->saveAs($fullOutputPath);
@ -849,13 +863,37 @@ class DocumentTemplateService extends BaseAdminService
$tableName = $config['table_name'];
$fieldName = $config['field_name'];
// 简单的数据库查询(实际应用中需要更完善的查询逻辑)
// 改进的数据库查询逻辑,支持条件查询
$model = \think\facade\Db::connect();
$result = $model->table($tableName)->field($fieldName)->find();
$query = $model->table($tableName);
// 如果有传入的查询条件(比如学员ID),使用条件查询
if (!empty($userFillData) && is_array($userFillData)) {
foreach ($userFillData as $key => $value) {
// 支持常见的查询字段
if (in_array($key, ['id', 'student_id', 'user_id', 'person_id', 'contract_id'])) {
$query->where($key, $value);
break; // 只使用第一个匹配的条件
}
}
}
$result = $query->field($fieldName)->find();
Log::info('数据库查询', [
'table' => $tableName,
'field' => $fieldName,
'result' => $result,
'user_data' => $userFillData
]);
return $result[$fieldName] ?? $config['default_value'] ?? '';
} catch (\Exception $e) {
Log::error('数据库查询失败:' . $e->getMessage());
Log::error('数据库查询失败:' . $e->getMessage(), [
'table' => $config['table_name'] ?? '',
'field' => $config['field_name'] ?? '',
'config' => $config
]);
return $config['default_value'] ?? '';
}
}
@ -1042,6 +1080,130 @@ class DocumentTemplateService extends BaseAdminService
return $this->pageQuery($searchModel);
}
/**
* 修复被Word格式化分割的占位符
* Word在编辑时会在占位符中插入格式化标签,导致{{placeholder}}被分割
* 这个方法会创建一个修复后的临时文件
*
* @param string $templatePath 原始模板文件路径
* @return string 修复后的模板文件路径
*/
private function fixBrokenPlaceholders(string $templatePath)
{
try {
// 创建临时文件
$tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'niucloud_templates';
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
$fixedTemplatePath = $tempDir . DIRECTORY_SEPARATOR . 'fixed_' . basename($templatePath);
// 复制原文件到临时位置
if (!copy($templatePath, $fixedTemplatePath)) {
Log::warning('无法创建临时模板文件,使用原始模板', ['template_path' => $templatePath]);
return $templatePath;
}
// 打开ZIP文件进行修复
$zip = new \ZipArchive();
if ($zip->open($fixedTemplatePath) === TRUE) {
$content = $zip->getFromName('word/document.xml');
if ($content !== false) {
$originalLength = strlen($content);
$fixedContent = $content;
// 第一步:修复跨XML标签的占位符(最常见的模式)
// 模式1:修复 <w:t>{{</w:t>...<w:t>内容}}</w:t> 这种分割
$pattern1 = '/<w:t[^>]*>\{\{<\/w:t>([^<]*)<w:t[^>]*>([^<]*)\}\}<\/w:t>/';
$fixedContent = preg_replace_callback($pattern1, function($matches) {
$placeholder = '{{' . $matches[1] . $matches[2] . '}}';
return '<w:t>' . $placeholder . '</w:t>';
}, $fixedContent);
// 模式2:修复 <w:t>{</w:t>...<w:t>{内容}}</w:t> 这种分割
$pattern2 = '/<w:t[^>]*>\{<\/w:t>([^<]*)<w:t[^>]*>\{([^<]*)\}\}<\/w:t>/';
$fixedContent = preg_replace_callback($pattern2, function($matches) {
$placeholder = '{{' . $matches[1] . $matches[2] . '}}';
return '<w:t>' . $placeholder . '</w:t>';
}, $fixedContent);
// 模式3:修复三段式分割 <w:t>{{</w:t>...<w:t>中间</w:t>...<w:t>}}</w:t>
$pattern3 = '/<w:t[^>]*>\{\{<\/w:t>([^<]*)<w:t[^>]*>([^<]*)<\/w:t>([^<]*)<w:t[^>]*>([^<]*)\}\}<\/w:t>/';
$fixedContent = preg_replace_callback($pattern3, function($matches) {
$placeholder = '{{' . $matches[1] . $matches[2] . $matches[4] . '}}';
return '<w:t>' . $placeholder . '</w:t>';
}, $fixedContent);
// 第二步:处理更复杂的格式化分割(包含rPr标签的)
// 匹配包含格式化信息的分割模式
$complexPattern = '/\{\{[^}]*?(?:<\/w:t><\/w:r><w:r[^>]*?>(?:<w:rPr[^>]*?>.*?<\/w:rPr>)?<w:t[^>]*?>)[^}]*?\}\}/s';
$fixedContent = preg_replace_callback($complexPattern, function($matches) {
$placeholder = $matches[0];
// 移除所有XML标签,保留纯文本
$cleaned = preg_replace('/<[^>]*?>/', '', $placeholder);
// 验证是否为有效占位符
if (!preg_match('/^\{\{[^}]+\}\}$/', $cleaned)) {
return $placeholder;
}
// 保持原有的第一个w:t标签结构
if (preg_match('/^([^<]*<w:t[^>]*>)/', $placeholder, $tagMatch)) {
return $tagMatch[1] . $cleaned . '</w:t>';
}
return $cleaned;
}, $fixedContent);
// 第三步:修复任何剩余的基本分割模式
$basicPattern = '/\{\{[^}]*?<[^>]*?>[^}]*?\}\}/';
$fixedContent = preg_replace_callback($basicPattern, function($matches) {
$placeholder = $matches[0];
$cleaned = preg_replace('/<[^>]*?>/', '', $placeholder);
if (!preg_match('/^\{\{[^}]+\}\}$/', $cleaned)) {
return $placeholder;
}
return $cleaned;
}, $fixedContent);
// 将修复后的内容写回ZIP文件
if ($zip->addFromString('word/document.xml', $fixedContent)) {
$zip->close();
Log::info('占位符修复完成', [
'template_file' => $templatePath,
'fixed_file' => $fixedTemplatePath,
'original_length' => $originalLength,
'fixed_length' => strlen($fixedContent),
'size_change' => strlen($fixedContent) - $originalLength
]);
return $fixedTemplatePath;
} else {
$zip->close();
Log::warning('无法写入修复后的内容,使用原始模板', ['template_path' => $templatePath]);
return $templatePath;
}
} else {
$zip->close();
Log::warning('无法读取document.xml,使用原始模板', ['template_path' => $templatePath]);
return $templatePath;
}
} else {
Log::warning('无法打开Word文档,使用原始模板', ['template_path' => $templatePath]);
return $templatePath;
}
} catch (\Exception $e) {
Log::error('修复占位符失败', [
'template_path' => $templatePath,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
// 修复失败不影响主流程,返回原始模板路径
return $templatePath;
}
}
/**
* 格式化文件大小
* @param int $size
@ -1431,4 +1593,545 @@ class DocumentTemplateService extends BaseAdminService
throw new \Exception('模板文档更新失败:' . $e->getMessage());
}
}
/**
* 使用XML字符串操作生成Word文档
* 此方法直接读取Word模板的XML内容,进行字符串替换,然后保存为新的Word文档
* @param array $data
* @return array
* @throws \Exception
*/
public function generateDocumentByXmlString(array $data)
{
$template = $this->contractModel->find($data['template_id']);
if (!$template) {
throw new \Exception('模板不存在');
}
if (empty($template['placeholder_config'])) {
throw new \Exception('模板尚未配置占位符');
}
// 创建生成记录
$logData = [
'site_id' => $this->site_id,
'template_id' => $data['template_id'],
'user_id' => $this->uid,
'user_type' => 1,
'fill_data' => json_encode($data['fill_data']),
'status' => 'pending',
'completed_at' => date('Y-m-d H:i:s')
];
$log = $this->logModel->create($logData);
try {
// 更新状态为处理中
$log->status = 'processing';
$log->process_start_time = date('Y-m-d H:i:s');
$log->save();
// 准备填充数据
$placeholderConfig = json_decode($template['placeholder_config'], true);
if (!empty($data['use_direct_values']) && $data['use_direct_values'] === true) {
$fillValues = $data['fill_data'];
Log::info('使用直接填充模式(XML字符串方法)', [
'template_id' => $data['template_id'],
'fill_data_count' => count($fillValues),
'fill_data_keys' => array_keys($fillValues)
]);
} else {
$fillValues = $this->prepareFillData($placeholderConfig, $data['fill_data']);
}
// 原始模板路径
$templatePath = public_path() . '/upload/' . $template['contract_template'];
$outputFileName = $data['output_filename'] ?: ($template['contract_name'] . '_' . date('YmdHis') . '.docx');
// 生成输出路径
$outputPath = 'generated_documents/' . date('Y/m/') . $outputFileName;
$publicOutputPath = public_path() . '/upload/' . $outputPath;
$publicOutputDir = dirname($publicOutputPath);
// 确保输出目录存在
if (!is_dir($publicOutputDir)) {
if (!mkdir($publicOutputDir, 0755, true) && !is_dir($publicOutputDir)) {
throw new \Exception('无法创建输出目录:' . $publicOutputDir);
}
}
// 使用XML字符串方法生成文档
$success = $this->processWordDocumentXml($templatePath, $publicOutputPath, $fillValues);
if (!$success) {
throw new \Exception('XML字符串处理失败');
}
// 更新生成记录
$log->status = 'completed';
$log->generated_file_path = $outputPath;
$log->generated_file_name = $outputFileName;
$log->process_end_time = date('Y-m-d H:i:s');
$log->save();
Log::info('XML字符串方法生成Word文档成功', [
'template_id' => $data['template_id'],
'output_path' => $outputPath,
'fill_values_count' => count($fillValues)
]);
return [
'log_id' => $log->id,
'file_path' => $outputPath,
'file_name' => $outputFileName,
'download_url' => url('/upload/' . $outputPath)
];
} catch (\Exception $e) {
// 更新记录为失败状态
$log->status = 'failed';
$log->error_msg = $e->getMessage();
$log->process_end_time = date('Y-m-d H:i:s');
$log->save();
Log::error('XML字符串方法生成Word文档失败', [
'template_id' => $data['template_id'],
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw new \Exception('文档生成失败:' . $e->getMessage());
}
}
/**
* 处理Word文档的XML内容进行占位符替换
* @param string $templatePath 模板文件路径
* @param string $outputPath 输出文件路径
* @param array $fillValues 填充值数组
* @return bool
* @throws \Exception
*/
private function processWordDocumentXml(string $templatePath, string $outputPath, array $fillValues): bool
{
try {
// 检查模板文件是否存在
if (!file_exists($templatePath)) {
throw new \Exception('模板文件不存在:' . $templatePath);
}
// 创建临时工作目录
$tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'word_processing_' . uniqid();
if (!mkdir($tempDir, 0755, true)) {
throw new \Exception('无法创建临时工作目录');
}
try {
// 1. 解压Word文档到临时目录
$zip = new \ZipArchive();
$result = $zip->open($templatePath);
if ($result !== TRUE) {
throw new \Exception('无法打开Word模板文件,错误代码:' . $result);
}
if (!$zip->extractTo($tempDir)) {
$zip->close();
throw new \Exception('无法解压Word模板文件');
}
$zip->close();
// 2. 读取document.xml文件
$documentXmlPath = $tempDir . DIRECTORY_SEPARATOR . 'word' . DIRECTORY_SEPARATOR . 'document.xml';
if (!file_exists($documentXmlPath)) {
throw new \Exception('Word文档缺少document.xml文件');
}
$xmlContent = file_get_contents($documentXmlPath);
if ($xmlContent === false) {
throw new \Exception('无法读取document.xml文件内容');
}
Log::info('读取XML内容成功', [
'original_length' => strlen($xmlContent),
'placeholders_to_replace' => array_keys($fillValues)
]);
// 3. 进行占位符替换
$modifiedXmlContent = $this->replaceXmlPlaceholders($xmlContent, $fillValues);
Log::info('XML占位符替换完成', [
'original_length' => strlen($xmlContent),
'modified_length' => strlen($modifiedXmlContent),
'size_change' => strlen($modifiedXmlContent) - strlen($xmlContent)
]);
// 4. 写回修改后的document.xml
if (file_put_contents($documentXmlPath, $modifiedXmlContent) === false) {
throw new \Exception('无法写入修改后的document.xml');
}
// 5. 重新打包为Word文档
$newZip = new \ZipArchive();
$result = $newZip->open($outputPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
if ($result !== TRUE) {
throw new \Exception('无法创建输出Word文件,错误代码:' . $result);
}
// 递归添加所有文件到ZIP
$this->addDirectoryToZip($newZip, $tempDir, '');
if (!$newZip->close()) {
throw new \Exception('无法保存输出Word文件');
}
Log::info('Word文档重新打包成功', [
'output_path' => $outputPath,
'file_size' => filesize($outputPath)
]);
return true;
} finally {
// 清理临时目录
$this->removeDirectory($tempDir);
}
} catch (\Exception $e) {
Log::error('处理Word文档XML失败', [
'template_path' => $templatePath,
'output_path' => $outputPath,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
/**
* 在XML内容中替换占位符
* @param string $xmlContent 原始XML内容
* @param array $fillValues 替换值数组
* @return string 替换后的XML内容
*/
private function replaceXmlPlaceholders(string $xmlContent, array $fillValues): string
{
$modifiedContent = $xmlContent;
$replacementCount = 0;
foreach ($fillValues as $placeholder => $value) {
// 确保占位符格式正确
$searchPattern = '{{' . $placeholder . '}}';
// 转义特殊字符,确保安全的XML替换
$safeValue = htmlspecialchars((string)$value, ENT_XML1, 'UTF-8');
// 进行替换
$beforeLength = strlen($modifiedContent);
$modifiedContent = str_replace($searchPattern, $safeValue, $modifiedContent, $count);
$afterLength = strlen($modifiedContent);
if ($count > 0) {
$replacementCount += $count;
Log::info('占位符替换成功', [
'placeholder' => $placeholder,
'value' => $value,
'safe_value' => $safeValue,
'replacement_count' => $count,
'content_length_change' => $afterLength - $beforeLength
]);
} else {
Log::warning('占位符未找到', [
'placeholder' => $placeholder,
'search_pattern' => $searchPattern
]);
}
}
Log::info('所有占位符处理完成', [
'total_replacements' => $replacementCount,
'processed_placeholders' => count($fillValues)
]);
return $modifiedContent;
}
/**
* 递归添加目录到ZIP文件
* @param \ZipArchive $zip ZIP文件对象
* @param string $sourcePath 源目录路径
* @param string $relativePath ZIP内的相对路径
* @return void
*/
private function addDirectoryToZip(\ZipArchive $zip, string $sourcePath, string $relativePath): void
{
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($sourcePath, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
$filePath = $file->getRealPath();
$relativeName = $relativePath . substr($filePath, strlen($sourcePath) + 1);
// 在Windows系统中统一使用正斜杠
$relativeName = str_replace('\\', '/', $relativeName);
if ($file->isDir()) {
// 添加目录
$zip->addEmptyDir($relativeName);
} else {
// 添加文件
$zip->addFile($filePath, $relativeName);
}
}
}
/**
* 递归删除目录
* @param string $dir 要删除的目录路径
* @return void
*/
private function removeDirectory(string $dir): void
{
if (!is_dir($dir)) {
return;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$filePath = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($filePath)) {
$this->removeDirectory($filePath);
} else {
unlink($filePath);
}
}
rmdir($dir);
}
/**
* 智能处理占位符,根据类型使用不同的处理方法
* @param TemplateProcessor $templateProcessor
* @param array $fillValues
* @param array $placeholderConfig
* @return void
*/
private function processPlaceholders(TemplateProcessor $templateProcessor, array $fillValues, array $placeholderConfig)
{
// 如果没有配置信息,使用简单的文本替换
if (empty($placeholderConfig)) {
foreach ($fillValues as $placeholder => $value) {
$templateProcessor->setValue($placeholder, $value);
}
return;
}
$placeholderConfig = is_string($placeholderConfig) ? json_decode($placeholderConfig, true) : $placeholderConfig;
foreach ($fillValues as $placeholder => $value) {
$config = $placeholderConfig[$placeholder] ?? [];
$fieldType = $config['field_type'] ?? 'text';
$dataType = $config['data_type'] ?? 'user_input';
Log::info('处理占位符', [
'placeholder' => $placeholder,
'value' => $value,
'field_type' => $fieldType,
'data_type' => $dataType
]);
try {
// 根据字段类型选择处理方式
if ($fieldType === 'image' || $dataType === 'sign_img' || $dataType === 'signature') {
// 处理图片类型
$this->setImageValue($templateProcessor, $placeholder, $value);
} else {
// 处理文本类型
$templateProcessor->setValue($placeholder, $value);
}
} catch (\Exception $e) {
Log::error('占位符处理失败', [
'placeholder' => $placeholder,
'value' => $value,
'error' => $e->getMessage()
]);
// 如果图片处理失败,尝试作为文本处理
$templateProcessor->setValue($placeholder, $value);
}
}
}
/**
* 设置图片占位符的值
* @param TemplateProcessor $templateProcessor
* @param string $placeholder
* @param mixed $value
* @return void
*/
private function setImageValue(TemplateProcessor $templateProcessor, string $placeholder, $value)
{
if (empty($value)) {
// 如果值为空,设置为空文本
$templateProcessor->setValue($placeholder, '');
return;
}
$imagePath = null;
try {
// 判断图片数据类型并处理
if (is_string($value)) {
if (str_starts_with($value, 'data:image/')) {
// 处理base64图片数据
$imagePath = $this->saveBase64Image($value);
} elseif (str_starts_with($value, 'http://') || str_starts_with($value, 'https://')) {
// 处理网络图片URL
$imagePath = $this->downloadImage($value);
} elseif (file_exists($value)) {
// 处理本地文件路径
$imagePath = $value;
} elseif (file_exists(public_path() . '/' . ltrim($value, '/'))) {
// 处理相对路径
$imagePath = public_path() . '/' . ltrim($value, '/');
}
}
if ($imagePath && file_exists($imagePath)) {
// 验证图片文件
$imageInfo = getimagesize($imagePath);
if ($imageInfo === false) {
throw new \Exception('无效的图片文件');
}
// 设置图片,限制尺寸
$templateProcessor->setImageValue($placeholder, [
'path' => $imagePath,
'width' => 100, // 可以根据需要调整
'height' => 100,
'ratio' => true
]);
Log::info('图片占位符设置成功', [
'placeholder' => $placeholder,
'image_path' => $imagePath,
'image_size' => $imageInfo
]);
// 如果是临时文件,标记稍后删除
if (str_contains($imagePath, sys_get_temp_dir())) {
register_shutdown_function(function() use ($imagePath) {
if (file_exists($imagePath)) {
@unlink($imagePath);
}
});
}
} else {
// 如果无法处理为图片,使用文本替换
$templateProcessor->setValue($placeholder, $value);
Log::warning('图片处理失败,使用文本替换', [
'placeholder' => $placeholder,
'value' => $value
]);
}
} catch (\Exception $e) {
Log::error('图片设置失败', [
'placeholder' => $placeholder,
'value' => $value,
'error' => $e->getMessage()
]);
// 如果图片处理失败,使用文本替换
$templateProcessor->setValue($placeholder, $value);
}
}
/**
* 保存base64图片数据到临时文件
* @param string $base64Data
* @return string|null
*/
private function saveBase64Image(string $base64Data): ?string
{
try {
// 解析base64数据
if (preg_match('/^data:image\/(\w+);base64,(.+)$/', $base64Data, $matches)) {
$imageType = $matches[1];
$imageData = base64_decode($matches[2]);
if ($imageData === false) {
throw new \Exception('base64解码失败');
}
// 创建临时文件
$tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'niucloud_images';
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
$fileName = uniqid('img_') . '.' . $imageType;
$filePath = $tempDir . DIRECTORY_SEPARATOR . $fileName;
if (file_put_contents($filePath, $imageData) === false) {
throw new \Exception('保存图片文件失败');
}
return $filePath;
}
} catch (\Exception $e) {
Log::error('保存base64图片失败:' . $e->getMessage());
}
return null;
}
/**
* 下载网络图片到临时文件
* @param string $url
* @return string|null
*/
private function downloadImage(string $url): ?string
{
try {
// 创建临时文件
$tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'niucloud_images';
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
$pathInfo = pathinfo(parse_url($url, PHP_URL_PATH));
$extension = $pathInfo['extension'] ?? 'jpg';
$fileName = uniqid('img_') . '.' . $extension;
$filePath = $tempDir . DIRECTORY_SEPARATOR . $fileName;
// 下载图片
$context = stream_context_create([
'http' => [
'timeout' => 30,
'user_agent' => 'Mozilla/5.0 (compatible; Document Generator)',
]
]);
$imageData = file_get_contents($url, false, $context);
if ($imageData === false) {
throw new \Exception('下载图片失败');
}
if (file_put_contents($filePath, $imageData) === false) {
throw new \Exception('保存图片文件失败');
}
// 验证是否为有效图片
if (getimagesize($filePath) === false) {
unlink($filePath);
throw new \Exception('下载的文件不是有效图片');
}
return $filePath;
} catch (\Exception $e) {
Log::error('下载图片失败:' . $e->getMessage(), ['url' => $url]);
}
return null;
}
}

36
niucloud/app/service/api/apiService/ContractSignFormService.php

@ -636,14 +636,43 @@ class ContractSignFormService extends BaseApiService
private function getSignSpecificFormFields($contract_sign_id, $student)
{
try {
// 从document_data_source_config表获取该签署关系专属的字段配置
// 先获取签署记录以得到合同ID
$sign_record = Db::table('school_contract_sign')
->where('id', $contract_sign_id)
->find();
if (!$sign_record) {
Log::error('签署记录不存在', ['contract_sign_id' => $contract_sign_id]);
return [];
}
$contract_id = $sign_record['contract_id'];
// 优先获取该签署关系专属的字段配置
$configs = Db::table('school_document_data_source_config')
->where('contract_sign_id', $contract_sign_id)
->select()
->toArray();
// 如果该签署记录没有专属配置,则获取合同模板的基础配置
if (empty($configs)) {
Log::warning('该签署记录无字段配置', ['contract_sign_id' => $contract_sign_id]);
Log::info('该签署记录无专属配置,使用合同模板基础配置', [
'contract_sign_id' => $contract_sign_id,
'contract_id' => $contract_id
]);
$configs = Db::table('school_document_data_source_config')
->where('contract_id', $contract_id)
->whereNull('contract_sign_id')
->select()
->toArray();
}
if (empty($configs)) {
Log::warning('该合同无字段配置', [
'contract_sign_id' => $contract_sign_id,
'contract_id' => $contract_id
]);
return [];
}
@ -736,8 +765,9 @@ class ContractSignFormService extends BaseApiService
switch ($table_name) {
case 'school_student':
case 'students': // 兼容配置中的简化表名
// 学员表:使用CoreFieldMappingService进行字段映射和转义
$service = new CoreFieldMappingService($config['table_name'], ['id' => $student['id']]);
$service = new CoreFieldMappingService('school_student', ['id' => $student['id']]);
// 配置枚举映射
$service->setFieldEnums([

4
uniapp/common/config.js

@ -1,6 +1,6 @@
// 环境变量配置
// const env = 'development'
const env = 'prod'
const env = 'development'
// const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式
const devurl = 'http://localhost:20080/api'

Loading…
Cancel
Save