智慧教务系统
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

<?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;
}
}
}