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.
212 lines
6.8 KiB
212 lines
6.8 KiB
<?php
|
|
namespace app\job\contract;
|
|
|
|
use core\base\BaseJob;
|
|
use app\model\contract\ContractSign;
|
|
use app\model\document\DocumentGenerateLog;
|
|
|
|
/**
|
|
* 文档生成队列任务
|
|
*/
|
|
class DocumentGenerateJobBasic extends BaseJob
|
|
{
|
|
/**
|
|
* 执行任务
|
|
* @param array $data 任务数据
|
|
* @return bool
|
|
*/
|
|
public function doJob(array $data): bool
|
|
{
|
|
$contractSignId = $data['contract_sign_id'];
|
|
|
|
try {
|
|
// 1. 获取合同签署信息
|
|
$contractSign = (new ContractSign())->find($contractSignId);
|
|
if (!$contractSign) {
|
|
throw new \Exception('合同签署记录不存在');
|
|
}
|
|
|
|
// 2. 获取填充数据
|
|
$fillData = json_decode($contractSign['fill_data'], true);
|
|
if (!$fillData) {
|
|
throw new \Exception('填充数据格式错误');
|
|
}
|
|
|
|
// 3. 生成Word文档
|
|
$generatedFile = $this->generateWordDocument($contractSign, $fillData);
|
|
|
|
// 4. 更新生成记录
|
|
$this->updateGenerateLog($contractSignId, 'completed', $generatedFile);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
$this->updateGenerateLog($contractSignId, 'failed', null, $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成Word文档
|
|
* @param ContractSign $contractSign 合同签署记录
|
|
* @param array $fillData 填充数据
|
|
* @return string 生成的文件路径
|
|
* @throws \Exception
|
|
*/
|
|
private function generateWordDocument(ContractSign $contractSign, array $fillData): string
|
|
{
|
|
// 1. 获取合同模板
|
|
$contract = \app\model\contract\Contract::find($contractSign['contract_id']);
|
|
if (!$contract) {
|
|
throw new \Exception('合同模板不存在');
|
|
}
|
|
|
|
$templatePath = public_path() . '/' . $contract['file_path'];
|
|
if (!file_exists($templatePath)) {
|
|
throw new \Exception('模板文件不存在:' . $templatePath);
|
|
}
|
|
|
|
// 2. 获取数据源配置
|
|
$configs = \app\model\document\DocumentDataSourceConfig::where('contract_id', $contractSign['contract_id'])
|
|
->select()
|
|
->toArray();
|
|
|
|
// 3. 构建替换数据
|
|
$replacements = [];
|
|
foreach ($configs as $config) {
|
|
$placeholder = '{{' . $config['placeholder'] . '}}';
|
|
$value = $fillData[$config['field_name']] ?? $config['default_value'] ?? '';
|
|
$replacements[$placeholder] = $this->formatValue($value, $config['field_type']);
|
|
}
|
|
|
|
// 4. 创建输出目录
|
|
$outputDir = 'generated/' . date('Y/m/d/');
|
|
$fullOutputDir = public_path() . '/' . $outputDir;
|
|
if (!is_dir($fullOutputDir)) {
|
|
mkdir($fullOutputDir, 0755, true);
|
|
}
|
|
|
|
// 5. 生成文件名
|
|
$fileName = time() . '_' . $contractSign['personnel_id'] . '_contract.docx';
|
|
$outputPath = $outputDir . $fileName;
|
|
$fullOutputPath = public_path() . '/' . $outputPath;
|
|
|
|
// 6. 使用PhpWord处理文档
|
|
try {
|
|
$phpWord = \PhpOffice\PhpWord\IOFactory::load($templatePath);
|
|
|
|
// 7. 替换占位符
|
|
$this->replaceDocumentPlaceholders($phpWord, $replacements);
|
|
|
|
// 8. 保存文档
|
|
$writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
|
|
$writer->save($fullOutputPath);
|
|
|
|
// 9. 验证文件是否生成成功
|
|
if (!file_exists($fullOutputPath)) {
|
|
throw new \Exception('文档生成失败');
|
|
}
|
|
|
|
return $outputPath;
|
|
|
|
} catch (\Exception $e) {
|
|
throw new \Exception('文档处理失败:' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 替换文档中的占位符
|
|
* @param \PhpOffice\PhpWord\PhpWord $phpWord
|
|
* @param array $replacements
|
|
*/
|
|
private function replaceDocumentPlaceholders(\PhpOffice\PhpWord\PhpWord $phpWord, array $replacements): void
|
|
{
|
|
foreach ($phpWord->getSections() as $section) {
|
|
foreach ($section->getElements() as $element) {
|
|
$this->replaceElementPlaceholders($element, $replacements);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 递归替换元素中的占位符
|
|
* @param mixed $element
|
|
* @param array $replacements
|
|
*/
|
|
private function replaceElementPlaceholders($element, array $replacements): void
|
|
{
|
|
if (method_exists($element, 'getElements')) {
|
|
foreach ($element->getElements() as $subElement) {
|
|
$this->replaceElementPlaceholders($subElement, $replacements);
|
|
}
|
|
}
|
|
|
|
if (method_exists($element, 'getText')) {
|
|
$text = $element->getText();
|
|
if (is_string($text)) {
|
|
$newText = str_replace(array_keys($replacements), array_values($replacements), $text);
|
|
if (method_exists($element, 'setText')) {
|
|
$element->setText($newText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 格式化值
|
|
* @param mixed $value 原始值
|
|
* @param string $type 字段类型
|
|
* @return string
|
|
*/
|
|
private function formatValue($value, string $type): string
|
|
{
|
|
switch ($type) {
|
|
case 'datetime':
|
|
if (is_numeric($value)) {
|
|
return date('Y-m-d H:i:s', $value);
|
|
} elseif (strtotime($value)) {
|
|
return date('Y-m-d H:i:s', strtotime($value));
|
|
}
|
|
break;
|
|
case 'date':
|
|
if (is_numeric($value)) {
|
|
return date('Y-m-d', $value);
|
|
} elseif (strtotime($value)) {
|
|
return date('Y-m-d', strtotime($value));
|
|
}
|
|
break;
|
|
case 'decimal':
|
|
return number_format((float)$value, 2);
|
|
case 'integer':
|
|
return (string)(int)$value;
|
|
default:
|
|
return (string)$value;
|
|
}
|
|
|
|
return (string)$value;
|
|
}
|
|
|
|
/**
|
|
* 更新生成记录
|
|
* @param int $contractSignId 合同签署ID
|
|
* @param string $status 状态
|
|
* @param string|null $generatedFile 生成的文件路径
|
|
* @param string|null $errorMsg 错误信息
|
|
*/
|
|
private function updateGenerateLog(int $contractSignId, string $status, ?string $generatedFile = null, ?string $errorMsg = null): void
|
|
{
|
|
$updateData = [
|
|
'status' => $status,
|
|
'completed_at' => time()
|
|
];
|
|
|
|
if ($generatedFile) {
|
|
$updateData['generated_file'] = $generatedFile;
|
|
}
|
|
|
|
if ($errorMsg) {
|
|
$updateData['error_msg'] = $errorMsg;
|
|
}
|
|
|
|
(new DocumentGenerateLog())->where('id', $contractSignId)->update($updateData);
|
|
}
|
|
}
|
|
|