Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
H
hh_ccs
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
贺阳
hh_ccs
Commits
065ffcc3
提交
065ffcc3
authored
10月 30, 2025
作者:
贺阳
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
定时清理向导生成的临时附件
上级
d9d75008
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
96 行增加
和
62 行删除
+96
-62
timer.xml
ccs_base/data/timer.xml
+14
-0
ai_image_edit_service.py
ccs_base/wizard/ai_image_edit_service.py
+0
-18
batch_get_pod_info_wizard.py
ccs_base/wizard/batch_get_pod_info_wizard.py
+79
-41
batch_get_pod_info_wizard_views.xml
ccs_base/wizard/batch_get_pod_info_wizard_views.xml
+3
-3
没有找到文件。
ccs_base/data/timer.xml
浏览文件 @
065ffcc3
...
@@ -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
ccs_base/wizard/ai_image_edit_service.py
浏览文件 @
065ffcc3
...
@@ -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
:
...
...
ccs_base/wizard/batch_get_pod_info_wizard.py
浏览文件 @
065ffcc3
...
@@ -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
,
...
...
ccs_base/wizard/batch_get_pod_info_wizard_views.xml
浏览文件 @
065ffcc3
...
@@ -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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论