|
|
|
import os
|
|
import zipfile
|
|
from datetime import datetime
|
|
from io import BytesIO
|
|
|
|
from flask import request, make_response, send_file
|
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
|
from flask_restful import Resource, reqparse
|
|
from app import db
|
|
from app.models import Customer
|
|
from app.models.translate import Translate
|
|
from app.utils.response import APIResponse
|
|
from app.utils.validators import (
|
|
validate_id_list
|
|
)
|
|
|
|
|
|
|
|
class AdminTranslateListResource(Resource):
|
|
@jwt_required()
|
|
def get(self):
|
|
"""获取翻译记录列表"""
|
|
|
|
parser = reqparse.RequestParser()
|
|
parser.add_argument('page', type=int, default=1, location='args')
|
|
parser.add_argument('limit', type=int, default=100, location='args')
|
|
parser.add_argument('status', type=str, location='args')
|
|
parser.add_argument('keyword', type=str, location='args')
|
|
args = parser.parse_args()
|
|
|
|
|
|
query = Translate.query.filter_by(
|
|
deleted_flag='N'
|
|
)
|
|
|
|
|
|
if args['status']:
|
|
valid_statuses = {'none', 'process', 'done', 'failed'}
|
|
if args['status'] not in valid_statuses:
|
|
return APIResponse.error(f"Invalid status value: {args['status']}"), 400
|
|
query = query.filter_by(status=args['status'])
|
|
|
|
if args['keyword']:
|
|
|
|
query = query.join(Customer, Translate.customer_id == Customer.id).filter(
|
|
(Translate.origin_filename.ilike(f"%{args['keyword']}%")) |
|
|
(Customer.email.ilike(f"%{args['keyword']}%"))
|
|
)
|
|
|
|
pagination = query.paginate(page=args['page'], per_page=args['limit'], error_out=False)
|
|
|
|
|
|
data = []
|
|
for t in pagination.items:
|
|
|
|
if t.start_at and t.end_at:
|
|
spend_time = t.end_at - t.start_at
|
|
spend_time_minutes = int(spend_time.total_seconds() // 60)
|
|
spend_time_seconds = int(spend_time.total_seconds() % 60)
|
|
spend_time_str = f"{spend_time_minutes}分{spend_time_seconds}秒"
|
|
else:
|
|
spend_time_str = "--"
|
|
|
|
|
|
customer = Customer.query.get(t.customer_id)
|
|
customer_email = customer.email if customer else "--"
|
|
customer_no = customer.customer_no if customer.customer_no else t.customer_id
|
|
|
|
start_at_str = t.start_at.strftime('%Y-%m-%d %H:%M:%S') if t.start_at else "--"
|
|
end_at_str = t.end_at.strftime('%Y-%m-%d %H:%M:%S') if t.end_at else "--"
|
|
|
|
|
|
data.append({
|
|
'id': t.id,
|
|
'customer_no': customer_no,
|
|
'customer_id': t.customer_id,
|
|
'customer_email': customer_email,
|
|
'origin_filename': t.origin_filename,
|
|
'status': t.status,
|
|
'process': float(t.process) if t.process is not None else None,
|
|
'start_at': start_at_str,
|
|
'end_at': end_at_str,
|
|
'spend_time': spend_time_str,
|
|
'lang': t.lang,
|
|
'target_filepath': t.target_filepath
|
|
})
|
|
|
|
|
|
return APIResponse.success({
|
|
'data': data,
|
|
'total': pagination.total,
|
|
'current_page': pagination.page
|
|
})
|
|
|
|
|
|
|
|
class AdminTranslateDownloadBatchResource(Resource):
|
|
@jwt_required()
|
|
def post(self):
|
|
"""批量下载多个翻译结果文件(管理员接口)"""
|
|
try:
|
|
|
|
data = request.get_json()
|
|
if not data or 'ids' not in data:
|
|
return {"message": "缺少 ids 参数"}, 400
|
|
|
|
ids = data['ids']
|
|
if not isinstance(ids, list):
|
|
return {"message": "ids 必须是数组"}, 400
|
|
|
|
|
|
records = Translate.query.filter(
|
|
Translate.id.in_(ids),
|
|
Translate.deleted_flag == 'N'
|
|
).all()
|
|
|
|
|
|
zip_buffer = BytesIO()
|
|
with zipfile.ZipFile(zip_buffer, 'w') as zip_file:
|
|
for record in records:
|
|
if record.target_filepath and os.path.exists(record.target_filepath):
|
|
|
|
zip_file.write(
|
|
record.target_filepath,
|
|
os.path.basename(record.target_filepath)
|
|
)
|
|
|
|
|
|
zip_buffer.seek(0)
|
|
|
|
|
|
return send_file(
|
|
zip_buffer,
|
|
mimetype='application/zip',
|
|
as_attachment=True,
|
|
download_name=f"translations_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
|
|
)
|
|
except Exception as e:
|
|
return {"message": f"服务器错误: {str(e)}"}, 500
|
|
|
|
|
|
|
|
class AdminTranslateDownloadResource(Resource):
|
|
|
|
def get(self, id):
|
|
"""通过 ID 下载单个翻译结果文件[^5]"""
|
|
|
|
translate = Translate.query.filter_by(
|
|
id=id,
|
|
|
|
).first_or_404()
|
|
|
|
|
|
if not translate.target_filepath or not os.path.exists(translate.target_filepath):
|
|
return APIResponse.error('文件不存在', 404)
|
|
|
|
|
|
response = make_response(send_file(
|
|
translate.target_filepath,
|
|
as_attachment=True,
|
|
download_name=os.path.basename(translate.target_filepath)
|
|
))
|
|
|
|
|
|
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
|
response.headers['Pragma'] = 'no-cache'
|
|
response.headers['Expires'] = '0'
|
|
|
|
return response
|
|
|
|
|
|
|
|
class AdminTranslateDeteleResource(Resource):
|
|
@jwt_required()
|
|
def delete(self, id):
|
|
"""删除单个翻译记录[^2]"""
|
|
try:
|
|
record = Translate.query.get_or_404(id)
|
|
db.session.delete(record)
|
|
db.session.commit()
|
|
return APIResponse.success(message='记录删除成功')
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return APIResponse.error('删除失败', 500)
|
|
|
|
|
|
class AdminTranslateBatchDeleteResource(Resource):
|
|
def post(self):
|
|
"""批量删除翻译记录[^3]"""
|
|
try:
|
|
ids = validate_id_list(request.json.get('ids'))
|
|
if len(ids) > 100:
|
|
return APIResponse.error('单次最多删除100条记录', 400)
|
|
|
|
Translate.query.filter(Translate.id.in_(ids)).delete()
|
|
db.session.commit()
|
|
return APIResponse.success(message=f'成功删除{len(ids)}条记录')
|
|
except APIResponse as e:
|
|
return e
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return APIResponse.error('批量删除失败', 500)
|
|
|
|
|
|
class AdminTranslateRestartResource(Resource):
|
|
def post(self, id):
|
|
"""重启翻译任务[^4]"""
|
|
try:
|
|
record = Translate.query.get_or_404(id)
|
|
if record.status not in ['failed', 'done']:
|
|
return APIResponse.error('当前状态无法重启', 400)
|
|
|
|
record.status = 'none'
|
|
record.start_at = None
|
|
record.end_at = None
|
|
record.failed_reason = None
|
|
db.session.commit()
|
|
return APIResponse.success(message='任务已重启')
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return APIResponse.error('重启失败', 500)
|
|
|
|
|
|
class AdminTranslateStatisticsResource(Resource):
|
|
def get(self):
|
|
"""获取翻译统计信息[^5]"""
|
|
try:
|
|
total = Translate.query.count()
|
|
done_count = Translate.query.filter_by(status='done').count()
|
|
processing_count = Translate.query.filter_by(status='process').count()
|
|
failed_count = Translate.query.filter_by(status='failed').count()
|
|
|
|
return APIResponse.success({
|
|
'total': total,
|
|
'done_count': done_count,
|
|
'processing_count': processing_count,
|
|
'failed_count': failed_count
|
|
})
|
|
except Exception as e:
|
|
return APIResponse.error('获取统计信息失败', 500)
|
|
|