提交 065ffcc3 authored 作者: 贺阳's avatar 贺阳

定时清理向导生成的临时附件

上级 d9d75008
...@@ -25,5 +25,18 @@ ...@@ -25,5 +25,18 @@
<field name="active" eval="True"/> <field name="active" eval="True"/>
</record> </record>
<!-- 清理向导生成的临时附件-->
<record id="cron_cleanup_temp_attachments" model="ir.cron">
<field name="name">清理向导临时附件</field>
<field name="model_id" ref="model_batch_get_pod_info_wizard"/>
<field name="state">code</field>
<field name="code">model.cron_cleanup_temp_attachments()</field>
<field name='interval_number'>1</field>
<field name='interval_type'>days</field>
<field name="nextcall" eval="(DateTime.now().replace(hour=0, minute=0) + timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')" />
<field name="numbercall">-1</field>
<field name="active" eval="True"/>
</record>
</data> </data>
</odoo> </odoo>
\ No newline at end of file
...@@ -32,7 +32,6 @@ class AIImageEditService: ...@@ -32,7 +32,6 @@ class AIImageEditService:
start_time = time.time() start_time = time.time()
_logger.info(f"开始AI图片编辑,目标文字: {text_to_remove}") _logger.info(f"开始AI图片编辑,目标文字: {text_to_remove}")
print(f"开始AI图片编辑,目标文字: {text_to_remove}") # 添加print输出
try: try:
# 根据要移除的文字构建不同的提示词,强调保持清晰度 # 根据要移除的文字构建不同的提示词,强调保持清晰度
...@@ -70,23 +69,18 @@ class AIImageEditService: ...@@ -70,23 +69,18 @@ class AIImageEditService:
if rsp.status_code == HTTPStatus.OK: if rsp.status_code == HTTPStatus.OK:
# 检查返回结果结构 # 检查返回结果结构
_logger.info(f"API响应结构: {rsp}") _logger.info(f"API响应结构: {rsp}")
print(f"API响应结构: {rsp}")
# 检查任务状态 # 检查任务状态
if hasattr(rsp, 'output') and hasattr(rsp.output, 'task_status'): if hasattr(rsp, 'output') and hasattr(rsp.output, 'task_status'):
task_status = rsp.output.task_status task_status = rsp.output.task_status
_logger.info(f"AI任务状态: {task_status}") _logger.info(f"AI任务状态: {task_status}")
print(f"AI任务状态: {task_status}")
if task_status == "FAILED": if task_status == "FAILED":
error_code = getattr(rsp.output, 'code', 'Unknown') error_code = getattr(rsp.output, 'code', 'Unknown')
error_message = getattr(rsp.output, 'message', 'Unknown error') error_message = getattr(rsp.output, 'message', 'Unknown error')
_logger.error(f"AI任务失败 - 错误码: {error_code}, 错误信息: {error_message}") _logger.error(f"AI任务失败 - 错误码: {error_code}, 错误信息: {error_message}")
print(f"AI任务失败 - 错误码: {error_code}, 错误信息: {error_message}")
return None return None
elif task_status != "SUCCEEDED": elif task_status != "SUCCEEDED":
_logger.warning(f"AI任务状态异常: {task_status}") _logger.warning(f"AI任务状态异常: {task_status}")
print(f"AI任务状态异常: {task_status}")
return None return None
# 安全地获取处理后图片的URL # 安全地获取处理后图片的URL
...@@ -94,35 +88,23 @@ class AIImageEditService: ...@@ -94,35 +88,23 @@ class AIImageEditService:
if hasattr(rsp, 'output') and hasattr(rsp.output, 'results') and len(rsp.output.results) > 0: if hasattr(rsp, 'output') and hasattr(rsp.output, 'results') and len(rsp.output.results) > 0:
image_url = rsp.output.results[0].url image_url = rsp.output.results[0].url
_logger.info(f"AI图片编辑成功,图片URL: {image_url}") _logger.info(f"AI图片编辑成功,图片URL: {image_url}")
print(f"AI图片编辑成功,图片URL: {image_url}") # 添加print输出
# 下载图片并转换为base64 # 下载图片并转换为base64
download_start_time = time.time()
edited_image_base64 = self.download_and_convert_to_base64(image_url) edited_image_base64 = self.download_and_convert_to_base64(image_url)
download_end_time = time.time()
download_time = download_end_time - download_start_time
_logger.info(f"图片下载耗时: {download_time:.2f}秒")
total_time = time.time() - start_time total_time = time.time() - start_time
_logger.info(f"AI图片编辑总耗时: {total_time:.2f}秒") _logger.info(f"AI图片编辑总耗时: {total_time:.2f}秒")
print(f"AI图片编辑总耗时: {total_time:.2f}秒") # 添加print输出
return edited_image_base64 return edited_image_base64
else: else:
_logger.error(f"API返回结果结构异常: {rsp}") _logger.error(f"API返回结果结构异常: {rsp}")
print(f"API返回结果结构异常: {rsp}")
return None return None
except (IndexError, AttributeError) as e: except (IndexError, AttributeError) as e:
_logger.error(f"解析API返回结果失败: {str(e)}") _logger.error(f"解析API返回结果失败: {str(e)}")
_logger.error(f"完整响应: {rsp}") _logger.error(f"完整响应: {rsp}")
print(f"解析API返回结果失败: {str(e)}")
print(f"完整响应: {rsp}")
return None return None
else: else:
_logger.error(f"AI图片编辑失败,HTTP返回码:{rsp.status_code}") _logger.error(f"AI图片编辑失败,HTTP返回码:{rsp.status_code}")
_logger.error(f"错误码:{rsp.code}") _logger.error(f"错误码:{rsp.code}")
_logger.error(f"错误信息:{rsp.message}") _logger.error(f"错误信息:{rsp.message}")
print(f"AI图片编辑失败,HTTP返回码:{rsp.status_code}") # 添加print输出
return None return None
except Exception as e: except Exception as e:
......
...@@ -8,10 +8,10 @@ import logging ...@@ -8,10 +8,10 @@ import logging
import time import time
import requests import requests
from odoo import models, fields, _ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from .ai_image_edit_service import AIImageEditService from .ai_image_edit_service import AIImageEditService
from datetime import datetime, timedelta
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -19,6 +19,82 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -19,6 +19,82 @@ class BatchGetPodInfoWizard(models.TransientModel):
_name = 'batch.get.pod.info.wizard' _name = 'batch.get.pod.info.wizard'
_description = 'Batch Get POD Info Wizard' # 批量获取POD信息向导 _description = 'Batch Get POD Info Wizard' # 批量获取POD信息向导
@api.model
def cron_cleanup_temp_attachments(self):
"""
定时清理向导生成的临时附件
每天早上8点执行,删除1天之前创建的temp_pod_开头的附件
"""
try:
# 计算1天前的时间(前一天23:59:59)
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
one_day_ago = today - timedelta(seconds=1) # 前一天23:59:59
_logger.info(f"开始执行定时清理临时附件任务,清理时间点: {one_day_ago.strftime('%Y-%m-%d %H:%M:%S')}")
# 使用SQL查询查找1天之前创建的临时附件
_logger.info("使用SQL查询查找临时附件")
# 构建SQL查询
sql_query = """
SELECT id, name, res_model, res_id, create_date, store_fname
FROM ir_attachment
WHERE res_model = 'batch.get.pod.info.wizard'
AND create_date < '%s'
ORDER BY create_date DESC
""" % (one_day_ago.strftime('%Y-%m-%d %H:%M:%S'))
# 执行SQL查询
self.env.cr.execute(sql_query)
sql_results = self.env.cr.fetchall()
# 将SQL结果转换为Odoo记录集
if sql_results:
attachment_ids = [result[0] for result in sql_results]
temp_attachments = self.env['ir.attachment'].sudo().browse(attachment_ids)
attachment_count = len(temp_attachments)
attachment_names = [att.name for att in temp_attachments]
_logger.info(f"找到 {attachment_count} 个{one_day_ago.strftime('%Y-%m-%d')}之前创建的临时附件,开始清理")
# 删除物理文件
for attachment in temp_attachments:
try:
# 获取附件的物理文件路径
if hasattr(attachment, 'store_fname') and attachment.store_fname:
# Odoo 12+ 使用 store_fname
file_path = attachment.store_fname
elif hasattr(attachment, 'datas_fname') and attachment.datas_fname:
# 旧版本使用 datas_fname
file_path = attachment.datas_fname
else:
# 尝试从 name 字段构建路径
file_path = attachment.name
# 构建完整的文件路径
import os
from odoo.tools import config
# 获取 Odoo 数据目录
data_dir = config.filestore(self.env.cr.dbname)
if data_dir and file_path:
full_path = os.path.join(data_dir, file_path)
if os.path.exists(full_path):
os.remove(full_path)
_logger.info(f"已删除物理文件: {full_path}")
else:
_logger.warning(f"物理文件不存在: {full_path}")
except Exception as file_e:
_logger.warning(f"删除物理文件失败 {attachment.name}: {str(file_e)}")
# 删除数据库记录
temp_attachments.unlink()
_logger.info(f"定时清理完成,共删除 {attachment_count} 个{one_day_ago.strftime('%Y-%m-%d')}之前创建的临时附件: {', '.join(attachment_names[:5])}{'...' if len(attachment_names) > 5 else ''}")
else:
_logger.info(f"没有找到{one_day_ago.strftime('%Y-%m-%d')}之前创建的临时附件需要清理")
except Exception as e:
_logger.error(f"定时清理临时附件失败: {str(e)}")
def get_order(self): def get_order(self):
""" """
得到单据 得到单据
...@@ -201,7 +277,7 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -201,7 +277,7 @@ class BatchGetPodInfoWizard(models.TransientModel):
} }
# 回写到附件信息 # 回写到附件信息
if processed_files: if processed_files and (self.sync_last_mile_pod or self.sync_match_node):
logging.info(f"回写PDF文件到清关文件,共 {len(processed_files)} 个文件") logging.info(f"回写PDF文件到清关文件,共 {len(processed_files)} 个文件")
# 回写PDF文件到清关文件 # 回写PDF文件到清关文件
self._write_pdf_file(processed_files) self._write_pdf_file(processed_files)
...@@ -821,29 +897,6 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -821,29 +897,6 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 转换为base64 # 转换为base64
img_base64 = base64.b64encode(img_data).decode('utf-8') img_base64 = base64.b64encode(img_data).decode('utf-8')
# 保存AI转换前的图片用于对比
try:
import os
from datetime import datetime
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
before_ai_filename = f"before_ai_{bl_no}_page{page_num + 1}_{timestamp}.png"
# 创建保存目录
save_dir = os.path.join(os.getcwd(), "ai_comparison_images")
if not os.path.exists(save_dir):
os.makedirs(save_dir)
before_ai_path = os.path.join(save_dir, before_ai_filename)
# 保存原始图片
with open(before_ai_path, 'wb') as f:
f.write(img_data)
_logger.info(f"已保存AI转换前的图片: {before_ai_path}")
print(f"已保存AI转换前的图片: {before_ai_path}") # 添加print输出
except Exception as e:
_logger.warning(f"保存AI转换前图片失败: {str(e)}")
print(f"保存AI转换前图片失败: {str(e)}") # 添加print输出
# 使用AI编辑图片,移除指定文字 # 使用AI编辑图片,移除指定文字
ai_start_time = time.time() ai_start_time = time.time()
edited_img_base64 = ai_service.edit_image_remove_text( edited_img_base64 = ai_service.edit_image_remove_text(
...@@ -856,21 +909,6 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -856,21 +909,6 @@ class BatchGetPodInfoWizard(models.TransientModel):
if edited_img_base64: if edited_img_base64:
# 解码base64图片数据 # 解码base64图片数据
edited_img_data = base64.b64decode(edited_img_base64) edited_img_data = base64.b64decode(edited_img_base64)
# 保存AI转换后的图片用于对比
try:
after_ai_filename = f"after_ai_{bl_no}_page{page_num + 1}_{timestamp}.png"
after_ai_path = os.path.join(save_dir, after_ai_filename)
# 保存AI处理后的图片
with open(after_ai_path, 'wb') as f:
f.write(edited_img_data)
_logger.info(f"已保存AI转换后的图片: {after_ai_path}")
print(f"已保存AI转换后的图片: {after_ai_path}") # 添加print输出
except Exception as e:
_logger.warning(f"保存AI转换后图片失败: {str(e)}")
print(f"保存AI转换后图片失败: {str(e)}") # 添加print输出
# 保存处理后的图片数据 # 保存处理后的图片数据
processed_pages.append({ processed_pages.append({
'img_data': edited_img_data, 'img_data': edited_img_data,
......
...@@ -28,16 +28,16 @@ ...@@ -28,16 +28,16 @@
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
<strong>Description:</strong> <!-- 说明: --> <strong>Description:</strong> <!-- 说明: -->
<ul> <ul>
<li> <li attrs="{'invisible': [('pdf_file', '!=', False)]}">
<strong>Sync Last Mile POD:</strong> <strong>Sync Last Mile POD:</strong>
Synchronize POD (Proof of Delivery) attachment information with TK system, including Synchronize POD (Proof of Delivery) attachment information with TK system, including
big package quantities and container numbers big package quantities and container numbers
</li> <!-- 同步尾程POD:向TK同步尾程交接POD(待大包数量和箱号)的附件信息 --> </li> <!-- 同步尾程POD:向TK同步尾程交接POD(待大包数量和箱号)的附件信息 -->
<li> <li attrs="{'invisible': ['|',('pdf_file', '=', False),('show_error_message', '!=', False)]}">
<strong>Remove Specified Text:</strong> <strong>Remove Specified Text:</strong>
Remove specified text (AGN, UCLINK LOGISITICS LTD) from PDF files Remove specified text (AGN, UCLINK LOGISITICS LTD) from PDF files
</li> <!-- 涂抹指定文字:对PDF文件中的指定文字进行涂抹处理 --> </li> <!-- 涂抹指定文字:对PDF文件中的指定文字进行涂抹处理 -->
<li> <li attrs="{'invisible': ['|',('pdf_file', '=', False),('show_error_message', '!=', False)]}">
<strong>Sync Push Match Node:</strong> <strong>Sync Push Match Node:</strong>
Synchronize and push matched node information based on POD file, extract time from Synchronize and push matched node information based on POD file, extract time from
red boxes as node operation time red boxes as node operation time
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论