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.
216 lines
6.9 KiB
216 lines
6.9 KiB
<?php
|
|
namespace app\service\admin\document;
|
|
|
|
use core\base\BaseAdminService;
|
|
use app\model\contract\Contract;
|
|
use app\model\document\DocumentDataSourceConfig;
|
|
use PhpOffice\PhpWord\IOFactory;
|
|
|
|
/**
|
|
* 文档模板服务类
|
|
*/
|
|
class DocumentTemplateServiceBasic extends BaseAdminService
|
|
{
|
|
/**
|
|
* 上传Word模板
|
|
* @param array $data 上传数据
|
|
* @return array 返回结果
|
|
* @throws \Exception
|
|
*/
|
|
public function uploadTemplate(array $data): array
|
|
{
|
|
// 1. 参数验证
|
|
if (empty($data['file'])) {
|
|
throw new \Exception('请选择要上传的文件');
|
|
}
|
|
|
|
$file = $data['file'];
|
|
if (!$file->isValid()) {
|
|
throw new \Exception('文件上传失败');
|
|
}
|
|
|
|
// 2. 文件类型验证
|
|
$ext = strtolower($file->getOriginalExtension());
|
|
if (!in_array($ext, ['docx'])) {
|
|
throw new \Exception('只支持.docx格式的文件');
|
|
}
|
|
|
|
// 3. 文件大小验证(10MB限制)
|
|
if ($file->getSize() > 10 * 1024 * 1024) {
|
|
throw new \Exception('文件大小不能超过10MB');
|
|
}
|
|
|
|
// 4. 创建上传目录
|
|
$uploadPath = 'upload/contract/' . date('Y/m/d/');
|
|
$fullDir = public_path() . '/' . $uploadPath;
|
|
if (!is_dir($fullDir)) {
|
|
if (!mkdir($fullDir, 0755, true)) {
|
|
throw new \Exception('创建上传目录失败');
|
|
}
|
|
}
|
|
|
|
// 5. 生成唯一文件名
|
|
$fileName = time() . '_' . uniqid() . '.' . $ext;
|
|
$fullPath = $uploadPath . $fileName;
|
|
$absolutePath = public_path() . '/' . $fullPath;
|
|
|
|
// 6. 移动文件
|
|
if (!$file->move($fullDir, $fileName)) {
|
|
throw new \Exception('文件保存失败');
|
|
}
|
|
|
|
// 7. 验证文件是否成功保存
|
|
if (!file_exists($absolutePath)) {
|
|
throw new \Exception('文件保存验证失败');
|
|
}
|
|
|
|
// 8. 解析占位符
|
|
$placeholders = $this->parsePlaceholders($absolutePath);
|
|
|
|
// 9. 保存合同记录
|
|
$contract = new Contract();
|
|
$contractData = [
|
|
'name' => $data['contract_name'] ?? '未命名合同',
|
|
'file_path' => $fullPath,
|
|
'status' => 1, // 启用状态
|
|
'type' => $data['contract_type'] ?? 'general',
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
$contractId = $contract->insertGetId($contractData);
|
|
if (!$contractId) {
|
|
// 如果保存失败,删除已上传的文件
|
|
unlink($absolutePath);
|
|
throw new \Exception('保存合同记录失败');
|
|
}
|
|
|
|
return [
|
|
'id' => $contractId,
|
|
'file_path' => $fullPath,
|
|
'placeholders' => $placeholders,
|
|
'file_size' => $file->getSize(),
|
|
'original_name' => $file->getOriginalName()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 解析Word文档中的占位符
|
|
* @param string $filePath 文件路径
|
|
* @return array 占位符列表
|
|
* @throws \Exception
|
|
*/
|
|
public function parsePlaceholders(string $filePath): array
|
|
{
|
|
try {
|
|
// 使用phpoffice/phpword解析文档
|
|
$phpWord = \PhpOffice\PhpWord\IOFactory::load($filePath);
|
|
$placeholders = [];
|
|
|
|
// 遍历所有section
|
|
foreach ($phpWord->getSections() as $section) {
|
|
$elements = $section->getElements();
|
|
foreach ($elements as $element) {
|
|
// 提取占位符逻辑
|
|
$this->extractPlaceholders($element, $placeholders);
|
|
}
|
|
}
|
|
|
|
return array_unique($placeholders);
|
|
} catch (\Exception $e) {
|
|
throw new \Exception('文档解析失败:' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 递归提取占位符
|
|
* @param mixed $element 文档元素
|
|
* @param array $placeholders 占位符数组
|
|
*/
|
|
private function extractPlaceholders($element, &$placeholders)
|
|
{
|
|
// 检查是否是文本元素
|
|
if (method_exists($element, 'getText')) {
|
|
$text = $element->getText();
|
|
if (is_string($text)) {
|
|
// 使用正则表达式提取{{占位符}}格式
|
|
preg_match_all('/\{\{([^}]+)\}\}/', $text, $matches);
|
|
if (!empty($matches[1])) {
|
|
$placeholders = array_merge($placeholders, $matches[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 如果元素包含子元素,递归处理
|
|
if (method_exists($element, 'getElements')) {
|
|
foreach ($element->getElements() as $subElement) {
|
|
$this->extractPlaceholders($subElement, $placeholders);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 配置数据源
|
|
* @param int $contractId 合同ID
|
|
* @param array $config 配置数据
|
|
* @return bool 是否成功
|
|
* @throws \Exception
|
|
*/
|
|
public function configDataSource(int $contractId, array $config): bool
|
|
{
|
|
// 1. 验证合同是否存在
|
|
$contract = (new Contract())->find($contractId);
|
|
if (!$contract) {
|
|
throw new \Exception('合同不存在');
|
|
}
|
|
|
|
// 2. 验证配置数据
|
|
if (empty($config) || !is_array($config)) {
|
|
throw new \Exception('配置数据不能为空');
|
|
}
|
|
|
|
// 3. 开启事务
|
|
\think\facade\Db::startTrans();
|
|
try {
|
|
// 4. 删除现有配置
|
|
(new DocumentDataSourceConfig())->where('contract_id', $contractId)->delete();
|
|
|
|
// 5. 批量插入新配置
|
|
$insertData = [];
|
|
foreach ($config as $item) {
|
|
// 验证必需字段
|
|
if (empty($item['placeholder'])) {
|
|
throw new \Exception('占位符不能为空');
|
|
}
|
|
|
|
$insertData[] = [
|
|
'contract_id' => $contractId,
|
|
'placeholder' => $item['placeholder'],
|
|
'table_name' => $item['table_name'] ?? '',
|
|
'field_name' => $item['field_name'] ?? '',
|
|
'field_type' => $item['field_type'] ?? 'string',
|
|
'is_required' => $item['is_required'] ?? 0,
|
|
'default_value' => $item['default_value'] ?? '',
|
|
'created_at' => date('Y-m-d H:i:s')
|
|
];
|
|
}
|
|
|
|
// 6. 批量插入
|
|
if (!empty($insertData)) {
|
|
$result = (new DocumentDataSourceConfig())->insertAll($insertData);
|
|
if (!$result) {
|
|
throw new \Exception('保存配置失败');
|
|
}
|
|
}
|
|
|
|
// 7. 提交事务
|
|
\think\facade\Db::commit();
|
|
return true;
|
|
|
|
} catch (\Exception $e) {
|
|
// 8. 回滚事务
|
|
\think\facade\Db::rollback();
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|