"""模板生成服务"""
import os
import datetime
from typing import Optional
from ..models import (
GenerateTemplateRequest,
GenerateTemplateResponse,
JxlsAnnotations,
DataStructInfo
)
from ..utils import ExcelHelper, JxlsHelper
from .validator import ParameterValidator
class TemplateGenerator:
"""模板生成服务类"""
def __init__(self, output_directory: str = "./templates"):
self.excel_helper = ExcelHelper()
self.jxls_helper = JxlsHelper()
self.validator = ParameterValidator()
self.output_directory = output_directory
# 确保输出目录存在
os.makedirs(self.output_directory, exist_ok=True)
def generate_template(self, request: GenerateTemplateRequest) -> GenerateTemplateResponse:
"""主要模板生成方法"""
try:
# 参数验证
self._validate_request(request)
# 生成文件路径
file_path = self._generate_file_path(request.templateName, request.outputPath)
# 根据数据格式生成模板
if request.dataFormat == "json":
area_annotation, each_annotation = self._create_json_template(request, file_path)
else: # array
area_annotation, each_annotation = self._create_array_template(request, file_path)
# 计算数据结构信息
column_count = len(request.dataStruct.dataFields)
last_cell = self.jxls_helper.calculate_last_cell(column_count, 2)
# 构建响应
jxls_annotations = JxlsAnnotations(
area=area_annotation,
each=each_annotation
)
data_struct_info = DataStructInfo(
collectName=request.dataStruct.collectName,
itemVariable=request.dataStruct.itemVariable,
columnCount=column_count,
lastCell=last_cell
)
return GenerateTemplateResponse.success_response(
template_path=file_path,
message="JXLS模板生成成功",
jxls_annotations=jxls_annotations,
data_struct=data_struct_info
)
except ValueError as e:
return GenerateTemplateResponse.error_response(
error="参数验证失败",
details=str(e)
)
except Exception as e:
return GenerateTemplateResponse.error_response(
error="模板生成失败",
details=str(e)
)
def _validate_request(self, request: GenerateTemplateRequest) -> None:
"""验证请求参数"""
# 基本参数验证
self.validator.validate_template_name(request.templateName)
self.validator.validate_data_struct(request.dataStruct, request.dataFormat)
self.validator.validate_variable_names(request.dataStruct)
if request.outputPath:
self.validator.validate_output_path(request.outputPath)
# 示例数据验证
if request.sampleData:
self.validator.validate_sample_data(
request.sampleData,
request.dataStruct,
request.dataFormat
)
def _create_json_template(
self,
request: GenerateTemplateRequest,
file_path: str
) -> tuple[str, str]:
"""创建JSON格式模板"""
# 创建工作簿
workbook = self.excel_helper.create_workbook()
worksheet = workbook.active
# 添加标题行
headers = [field.name for field in request.dataStruct.dataFields]
self.excel_helper.add_headers(worksheet, headers)
# 添加数据行
self.excel_helper.add_data_row(
worksheet,
request.dataStruct,
request.dataFormat,
row=2
)
# 应用JXLS批注
area_annotation, each_annotation = self.jxls_helper.apply_jxls_annotations(
worksheet,
request.dataStruct.collectName,
request.dataStruct.itemVariable,
len(request.dataStruct.dataFields)
)
# 保存文件
self.excel_helper.save_workbook(workbook, file_path)
return area_annotation, each_annotation
def _create_array_template(
self,
request: GenerateTemplateRequest,
file_path: str
) -> tuple[str, str]:
"""创建数组格式模板"""
# 创建工作簿
workbook = self.excel_helper.create_workbook()
worksheet = workbook.active
# 添加标题行
headers = [field.name for field in request.dataStruct.dataFields]
self.excel_helper.add_headers(worksheet, headers)
# 添加数据行
self.excel_helper.add_data_row(
worksheet,
request.dataStruct,
request.dataFormat,
row=2
)
# 应用JXLS批注
area_annotation, each_annotation = self.jxls_helper.apply_jxls_annotations(
worksheet,
request.dataStruct.collectName,
request.dataStruct.itemVariable,
len(request.dataStruct.dataFields)
)
# 保存文件
self.excel_helper.save_workbook(workbook, file_path)
return area_annotation, each_annotation
def _generate_file_path(
self,
template_name: str,
output_path: Optional[str] = None
) -> str:
"""生成文件路径"""
if output_path:
# 规范化路径
normalized_path = os.path.normpath(output_path)
# 检查是否为目录路径
is_directory_path = (
normalized_path.endswith(os.sep) or
normalized_path.endswith('/') or
normalized_path.endswith('\\') or
(os.path.exists(normalized_path) and os.path.isdir(normalized_path)) or
# 如果路径不包含扩展名且不存在,则假定为目录路径
(not os.path.exists(normalized_path) and not os.path.splitext(normalized_path)[1])
)
if is_directory_path:
# 路径是目录,使用默认文件名
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{template_name}_{timestamp}.xlsx"
file_path = os.path.join(normalized_path, filename)
# 确保目录存在
if not os.path.exists(normalized_path):
os.makedirs(normalized_path, exist_ok=True)
else:
# 路径包含文件名,直接使用
file_path = normalized_path
# 确保有.xlsx扩展名
if not file_path.lower().endswith('.xlsx'):
file_path += '.xlsx'
# 确保目录存在
directory = os.path.dirname(file_path)
if directory and not os.path.exists(directory):
os.makedirs(directory, exist_ok=True)
else:
# 使用默认输出目录和时间戳文件名
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{template_name}_{timestamp}.xlsx"
file_path = os.path.join(self.output_directory, filename)
# 转换为绝对路径
return os.path.abspath(file_path)