666 lines
26 KiB
Python
666 lines
26 KiB
Python
from flask import request, jsonify, current_app
|
||
from datetime import datetime
|
||
import os
|
||
import hashlib
|
||
from werkzeug.utils import secure_filename
|
||
from app import db
|
||
from app.models import Version, Product, Device
|
||
from . import api_bp
|
||
from .decorators import require_login, require_admin
|
||
from sqlalchemy import desc, func, case, or_
|
||
import traceback
|
||
import sys
|
||
from app.utils.logger import log_operation
|
||
|
||
@api_bp.route('/versions', methods=['GET'])
|
||
@require_login
|
||
def get_versions():
|
||
"""获取版本列表"""
|
||
try:
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = min(request.args.get('per_page', 20, type=int), 100)
|
||
product_id = request.args.get('product_id')
|
||
status = request.args.get('publish_status', type=int)
|
||
keyword = request.args.get('keyword', '').strip()
|
||
include_stats = request.args.get('include_stats', 'true').lower() == 'true'
|
||
|
||
query = Version.query
|
||
|
||
if product_id:
|
||
query = query.filter_by(product_id=product_id)
|
||
if status is not None:
|
||
query = query.filter_by(publish_status=status)
|
||
if keyword:
|
||
query = query.filter(Version.version_num.like(f'%{keyword}%'))
|
||
|
||
query = query.order_by(desc(Version.create_time))
|
||
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
||
|
||
versions = pagination.items
|
||
|
||
# 优化:批量查询统计信息,避免N+1查询问题
|
||
if include_stats and versions:
|
||
# 构建版本查询条件
|
||
version_conditions = []
|
||
for v in versions:
|
||
# 确保product_id和version_num都存在
|
||
if v.product_id and v.version_num:
|
||
version_conditions.append(
|
||
(Device.product_id == v.product_id) &
|
||
(Device.software_version == v.version_num)
|
||
)
|
||
|
||
if version_conditions:
|
||
# 批量查询设备统计
|
||
device_stats = db.session.query(
|
||
Device.product_id,
|
||
Device.software_version,
|
||
func.count(Device.device_id).label('download_count'),
|
||
func.sum(case((Device.status == 1, 1), else_=0)).label('active_device_count')
|
||
).filter(
|
||
or_(*version_conditions)
|
||
).group_by(Device.product_id, Device.software_version).all()
|
||
|
||
# 构建统计字典
|
||
stats_dict = {}
|
||
for stat in device_stats:
|
||
key = (stat.product_id, stat.software_version)
|
||
stats_dict[key] = {
|
||
'download_count': stat.download_count or 0,
|
||
'active_device_count': stat.active_device_count or 0
|
||
}
|
||
|
||
# 为每个版本添加统计信息
|
||
for version in versions:
|
||
key = (version.product_id, version.version_num)
|
||
stats = stats_dict.get(key, {'download_count': 0, 'active_device_count': 0})
|
||
version._cached_stats = stats
|
||
|
||
versions = [version.to_dict(include_stats=include_stats) for version in versions]
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'data': {
|
||
'versions': versions,
|
||
'pagination': {
|
||
'page': page,
|
||
'per_page': per_page,
|
||
'total': pagination.total,
|
||
'pages': pagination.pages
|
||
}
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"获取版本列表失败: {str(e)}")
|
||
return jsonify({'success': False, 'message': '服务器内部错误'}), 500
|
||
|
||
@api_bp.route('/versions', methods=['POST'])
|
||
@require_login
|
||
def create_version():
|
||
"""创建版本"""
|
||
try:
|
||
current_app.logger.info("开始创建版本")
|
||
current_app.logger.info(f"请求的Content-Type: {request.content_type}")
|
||
current_app.logger.info(f"请求方法: {request.method}")
|
||
current_app.logger.info(f"请求URL: {request.url}")
|
||
current_app.logger.info(f"请求头: {dict(request.headers)}")
|
||
|
||
# 处理文件上传和表单数据
|
||
if request.content_type and ('multipart/form-data' in request.content_type or 'application/x-www-form-urlencoded' in request.content_type):
|
||
current_app.logger.info("处理表单数据请求")
|
||
# 处理表单数据
|
||
product_id = request.form.get('product_id')
|
||
version_num = request.form.get('version_num')
|
||
update_log = request.form.get('update_log', '')
|
||
download_url = request.form.get('download_url', '')
|
||
file_hash = request.form.get('file_hash', '')
|
||
force_update = request.form.get('force_update', 0)
|
||
download_status = request.form.get('download_status', 1)
|
||
min_license_version = request.form.get('min_license_version')
|
||
# 添加对新增字段的处理
|
||
platform = request.form.get('platform', '')
|
||
description = request.form.get('description', '')
|
||
publish_now = request.form.get('publish_now') == 'true' or request.form.get('publish_now') == 'on'
|
||
else:
|
||
current_app.logger.info("处理JSON请求")
|
||
# 处理JSON数据
|
||
data = request.get_json()
|
||
if not data:
|
||
current_app.logger.warning("请求数据为空")
|
||
return jsonify({'success': False, 'message': '请求数据为空'}), 400
|
||
|
||
product_id = data.get('product_id')
|
||
version_num = data.get('version_num')
|
||
update_log = data.get('update_log', '')
|
||
download_url = data.get('download_url', '')
|
||
file_hash = data.get('file_hash', '')
|
||
force_update = data.get('force_update', 0)
|
||
download_status = data.get('download_status', 1)
|
||
min_license_version = data.get('min_license_version')
|
||
# 添加对新增字段的处理
|
||
platform = data.get('platform', '')
|
||
description = data.get('description', '')
|
||
publish_now = data.get('publish_now', False)
|
||
|
||
current_app.logger.info(f"收到的参数: product_id={product_id}, version_num={version_num}")
|
||
|
||
if not all([product_id, version_num]):
|
||
current_app.logger.warning(f"缺少必要参数: product_id={product_id}, version_num={version_num}")
|
||
return jsonify({'success': False, 'message': '缺少必要参数'}), 400
|
||
|
||
current_app.logger.info(f"验证产品是否存在: product_id={product_id}")
|
||
# 验证产品存在
|
||
current_app.logger.info(f"执行产品查询...")
|
||
|
||
# 先查询所有产品进行调试
|
||
all_products = Product.query.all()
|
||
current_app.logger.info(f"数据库中所有产品: {[(p.product_id, p.product_name) for p in all_products]}")
|
||
|
||
# 执行实际查询
|
||
product = Product.query.filter_by(product_id=product_id).first()
|
||
current_app.logger.info(f"产品查询结果: {product}")
|
||
if not product:
|
||
current_app.logger.warning(f"产品不存在: product_id={product_id}")
|
||
return jsonify({'success': False, 'message': '产品不存在'}), 404
|
||
|
||
current_app.logger.info(f"检查版本号是否重复: product_id={product_id}, version_num={version_num}")
|
||
# 检查版本号是否重复
|
||
existing = Version.query.filter_by(
|
||
product_id=product_id,
|
||
version_num=version_num
|
||
).first()
|
||
if existing:
|
||
current_app.logger.warning(f"版本号已存在: product_id={product_id}, version_num={version_num}")
|
||
return jsonify({'success': False, 'message': '版本号已存在'}), 400
|
||
|
||
# 验证文件链接格式(如果提供了链接)
|
||
if download_url:
|
||
from urllib.parse import urlparse
|
||
try:
|
||
result = urlparse(download_url)
|
||
if not all([result.scheme, result.netloc]):
|
||
current_app.logger.warning(f"无效的文件链接格式: {download_url}")
|
||
return jsonify({'success': False, 'message': '文件链接格式无效'}), 400
|
||
except Exception as e:
|
||
current_app.logger.warning(f"文件链接验证失败: {str(e)}")
|
||
return jsonify({'success': False, 'message': '文件链接格式无效'}), 400
|
||
|
||
current_app.logger.info(f"创建版本对象: product_id={product_id}, version_num={version_num}")
|
||
version = Version(
|
||
product_id=product_id,
|
||
version_num=version_num,
|
||
platform=platform, # 添加新字段
|
||
description=description, # 添加新字段
|
||
update_log=update_log,
|
||
download_url=download_url,
|
||
file_hash=file_hash,
|
||
force_update=force_update,
|
||
download_status=download_status,
|
||
min_license_version=min_license_version
|
||
)
|
||
|
||
current_app.logger.info("添加版本到数据库")
|
||
db.session.add(version)
|
||
current_app.logger.info("提交数据库事务")
|
||
db.session.commit()
|
||
|
||
current_app.logger.info(f"检查是否立即发布: publish_now={publish_now}")
|
||
# 如果选择了立即发布,则发布版本
|
||
if publish_now:
|
||
current_app.logger.info("发布版本")
|
||
version.publish()
|
||
|
||
# 记录操作日志
|
||
log_operation('CREATE_VERSION', 'VERSION', version.version_id, {
|
||
'product_id': version.product_id,
|
||
'version_num': version.version_num,
|
||
'publish_now': publish_now,
|
||
'has_download_url': bool(download_url)
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '版本创建成功',
|
||
'data': version.to_dict()
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
current_app.logger.error(f"创建版本失败: {str(e)}")
|
||
# 添加更详细的错误信息
|
||
import traceback
|
||
current_app.logger.error(f"创建版本失败详细信息: {traceback.format_exc()}")
|
||
return jsonify({'success': False, 'message': f'服务器内部错误: {str(e)}'}), 500
|
||
|
||
@api_bp.route('/versions/<int:version_id>/publish', methods=['POST'])
|
||
@require_login
|
||
def publish_version(version_id):
|
||
"""发布版本"""
|
||
try:
|
||
version = Version.query.get(version_id)
|
||
if not version:
|
||
return jsonify({'success': False, 'message': '版本不存在'}), 404
|
||
|
||
version.publish()
|
||
|
||
# 记录操作日志
|
||
log_operation('PUBLISH_VERSION', 'VERSION', version.version_id, {
|
||
'version_num': version.version_num
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '版本发布成功',
|
||
'data': version.to_dict()
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"发布版本失败: {str(e)}")
|
||
return jsonify({'success': False, 'message': '服务器内部错误'}), 500
|
||
|
||
@api_bp.route('/versions/<int:version_id>', methods=['PUT'])
|
||
@require_login
|
||
def update_version(version_id):
|
||
"""更新版本信息"""
|
||
try:
|
||
version = Version.query.get(version_id)
|
||
if not version:
|
||
return jsonify({'success': False, 'message': '版本不存在'}), 404
|
||
|
||
# 获取请求数据
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({'success': False, 'message': '请求数据为空'}), 400
|
||
|
||
# 更新版本信息(只允许更新未发布或已回滚的版本的部分信息)
|
||
# 已发布的版本只能更新描述信息
|
||
version.platform = data.get('platform', version.platform)
|
||
version.description = data.get('description', version.description)
|
||
version.update_log = data.get('update_log', version.update_log)
|
||
version.min_license_version = data.get('min_license_version', version.min_license_version)
|
||
version.force_update = data.get('force_update', version.force_update)
|
||
version.download_status = data.get('download_status', version.download_status)
|
||
|
||
# 只有未发布或已回滚的版本才能更新版本号和产品
|
||
if version.publish_status != 1: # 不是已发布状态
|
||
version.version_num = data.get('version_num', version.version_num)
|
||
version.product_id = data.get('product_id', version.product_id)
|
||
|
||
# 保存更新
|
||
db.session.commit()
|
||
|
||
# 记录操作日志
|
||
log_operation('UPDATE_VERSION', 'VERSION', version.version_id, {
|
||
'version_num': version.version_num,
|
||
'platform': version.platform,
|
||
'description': version.description
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '版本信息更新成功',
|
||
'data': version.to_dict()
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
current_app.logger.error(f"更新版本信息失败: {str(e)}")
|
||
return jsonify({'success': False, 'message': '服务器内部错误'}), 500
|
||
|
||
|
||
@api_bp.route('/versions/<int:version_id>/status', methods=['PUT'])
|
||
@require_login
|
||
def update_version_status(version_id):
|
||
"""更新版本状态(发布/取消发布)"""
|
||
try:
|
||
version = Version.query.get(version_id)
|
||
if not version:
|
||
return jsonify({'success': False, 'message': '版本不存在'}), 404
|
||
|
||
# 获取请求数据
|
||
data = request.get_json()
|
||
status = data.get('status')
|
||
|
||
if status is None:
|
||
return jsonify({'success': False, 'message': '缺少状态参数'}), 400
|
||
|
||
# 更新状态
|
||
if status == 1: # 发布
|
||
version.publish()
|
||
message = '版本发布成功'
|
||
elif status == 0: # 取消发布
|
||
version.unpublish()
|
||
message = '版本取消发布成功'
|
||
elif status == 2: # 回滚
|
||
version.rollback()
|
||
message = '版本回滚成功'
|
||
else:
|
||
return jsonify({'success': False, 'message': '无效的状态值'}), 400
|
||
|
||
# 记录操作日志
|
||
status_names = {0: '取消发布', 1: '发布', 2: '回滚'}
|
||
log_operation('UPDATE_VERSION_STATUS', 'VERSION', version.version_id, {
|
||
'version_num': version.version_num,
|
||
'status': status,
|
||
'status_name': status_names.get(status, '未知')
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': message,
|
||
'data': version.to_dict()
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"更新版本状态失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '服务器内部错误'
|
||
}), 500
|
||
|
||
@api_bp.route('/versions/upload', methods=['POST'])
|
||
@require_login
|
||
def upload_version_file():
|
||
"""上传版本文件"""
|
||
try:
|
||
# 检查是否有文件上传
|
||
if 'version_file' not in request.files:
|
||
return jsonify({'success': False, 'message': '没有文件被上传'}), 400
|
||
|
||
file = request.files['version_file']
|
||
if file.filename == '' or file.filename is None:
|
||
return jsonify({'success': False, 'message': '没有选择文件'}), 400
|
||
|
||
if file:
|
||
# 确保上传目录存在
|
||
upload_folder = current_app.config['UPLOAD_FOLDER']
|
||
# 确保使用绝对路径
|
||
upload_folder = os.path.abspath(upload_folder)
|
||
os.makedirs(upload_folder, exist_ok=True)
|
||
|
||
# 生成安全的文件名
|
||
filename = secure_filename(str(file.filename))
|
||
# 在文件名前添加时间戳以避免重复
|
||
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
|
||
filename = f"{timestamp}_{filename}"
|
||
|
||
# 保存文件
|
||
file_path = os.path.join(upload_folder, filename)
|
||
file.save(file_path)
|
||
|
||
# 计算文件哈希值
|
||
file_hash = calculate_file_hash(file_path)
|
||
|
||
# 生成文件访问URL(相对于static目录)
|
||
# 修复URL生成逻辑
|
||
file_url = f"/static/uploads/{filename}"
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '文件上传成功',
|
||
'data': {
|
||
'file_name': filename,
|
||
'file_url': file_url,
|
||
'file_hash': file_hash,
|
||
'file_size': os.path.getsize(file_path)
|
||
}
|
||
})
|
||
else:
|
||
return jsonify({'success': False, 'message': '文件上传失败'}), 400
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"文件上传失败: {str(e)}")
|
||
import traceback
|
||
current_app.logger.error(f"文件上传失败详细信息: {traceback.format_exc()}")
|
||
return jsonify({'success': False, 'message': f'文件上传失败: {str(e)}'}), 500
|
||
|
||
def calculate_file_hash(file_path):
|
||
"""计算文件SHA256哈希值"""
|
||
try:
|
||
hash_sha256 = hashlib.sha256()
|
||
with open(file_path, "rb") as f:
|
||
for chunk in iter(lambda: f.read(4096), b""):
|
||
hash_sha256.update(chunk)
|
||
return hash_sha256.hexdigest()
|
||
except Exception as e:
|
||
raise Exception(f"计算文件哈希值失败: {str(e)}")
|
||
|
||
def extract_filename_from_url(download_url):
|
||
"""从下载URL中提取文件名"""
|
||
if not download_url:
|
||
return None
|
||
|
||
# 从URL中提取文件名,假设URL格式为 /static/uploads/filename
|
||
if '/static/uploads/' in download_url:
|
||
filename = download_url.split('/static/uploads/')[-1]
|
||
return filename
|
||
return None
|
||
|
||
@api_bp.route('/versions/<int:version_id>', methods=['DELETE'])
|
||
@require_login
|
||
def delete_version(version_id):
|
||
"""删除版本"""
|
||
try:
|
||
version = Version.query.get(version_id)
|
||
if not version:
|
||
return jsonify({'success': False, 'message': '版本不存在'}), 404
|
||
|
||
# 检查版本是否已被发布,已发布的版本不能直接删除
|
||
if version.is_published():
|
||
return jsonify({'success': False, 'message': '已发布的版本不能直接删除,请先取消发布后再删除'}), 400
|
||
|
||
# 检查是否有设备正在使用此版本
|
||
active_device_count = version.get_active_device_count()
|
||
if active_device_count > 0:
|
||
return jsonify({'success': False, 'message': f'有{active_device_count}个活跃设备正在使用此版本,为保护数据安全,不能直接删除。请先将这些设备解绑或禁用后再删除版本'}), 400
|
||
|
||
# 删除版本相关的文件
|
||
if version.download_url:
|
||
filename = extract_filename_from_url(version.download_url)
|
||
if filename:
|
||
upload_folder = os.path.abspath(current_app.config['UPLOAD_FOLDER'])
|
||
file_path = os.path.join(upload_folder, filename)
|
||
if os.path.exists(file_path):
|
||
try:
|
||
os.remove(file_path)
|
||
current_app.logger.info(f"已删除版本文件: {file_path}")
|
||
except Exception as e:
|
||
current_app.logger.error(f"删除版本文件失败: {str(e)}")
|
||
|
||
# 删除版本
|
||
db.session.delete(version)
|
||
db.session.commit()
|
||
|
||
# 记录操作日志
|
||
log_operation('DELETE_VERSION', 'VERSION', version.version_id, {
|
||
'version_num': version.version_num
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '版本删除成功'
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
current_app.logger.error(f"删除版本失败: {str(e)}")
|
||
return jsonify({'success': False, 'message': '服务器内部错误'}), 500
|
||
|
||
|
||
@api_bp.route('/versions/batch', methods=['DELETE'])
|
||
@require_login
|
||
def batch_delete_versions():
|
||
"""批量删除版本"""
|
||
try:
|
||
data = request.get_json()
|
||
if not data or 'version_ids' not in data:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '请求数据为空或缺少version_ids字段'
|
||
}), 400
|
||
|
||
version_ids = data['version_ids']
|
||
if not isinstance(version_ids, list) or len(version_ids) == 0:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': 'version_ids必须是非空列表'
|
||
}), 400
|
||
|
||
# 查找所有要删除的版本
|
||
versions = Version.query.filter(Version.version_id.in_(version_ids)).all()
|
||
if len(versions) != len(version_ids):
|
||
found_ids = [v.version_id for v in versions]
|
||
missing_ids = [vid for vid in version_ids if vid not in found_ids]
|
||
return jsonify({
|
||
'success': False,
|
||
'message': f'以下版本不存在: {", ".join(map(str, missing_ids))}'
|
||
}), 404
|
||
|
||
# 检查是否有已发布的版本
|
||
published_versions = [v for v in versions if v.is_published()]
|
||
if published_versions:
|
||
published_ids = [str(v.version_id) for v in published_versions]
|
||
return jsonify({
|
||
'success': False,
|
||
'message': f'以下版本已发布,不能直接删除: {", ".join(published_ids)},请先取消发布后再删除'
|
||
}), 400
|
||
|
||
# 检查是否有设备正在使用这些版本
|
||
undeletable_versions = []
|
||
for version in versions:
|
||
active_device_count = version.get_active_device_count()
|
||
if active_device_count > 0:
|
||
undeletable_versions.append({
|
||
'version_id': version.version_id,
|
||
'version_num': version.version_num,
|
||
'active_device_count': active_device_count
|
||
})
|
||
|
||
if undeletable_versions:
|
||
undeletable_info = [f'{v["version_num"]}({v["active_device_count"]}个设备)' for v in undeletable_versions]
|
||
return jsonify({
|
||
'success': False,
|
||
'message': f'以下版本有活跃设备正在使用,不能直接删除: {", ".join(undeletable_info)},请先将这些设备解绑或禁用后再删除版本'
|
||
}), 400
|
||
|
||
# 批量删除版本
|
||
for version in versions:
|
||
# 删除版本相关的文件
|
||
if version.download_url:
|
||
filename = extract_filename_from_url(version.download_url)
|
||
if filename:
|
||
file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
|
||
# 检查文件是否存在,如果存在则删除
|
||
if os.path.exists(file_path):
|
||
try:
|
||
os.remove(file_path)
|
||
current_app.logger.info(f"已删除版本文件: {file_path}")
|
||
except Exception as e:
|
||
current_app.logger.error(f"删除版本文件失败: {str(e)}")
|
||
# 即使文件删除失败也继续删除版本记录
|
||
|
||
db.session.delete(version)
|
||
|
||
db.session.commit()
|
||
|
||
# 记录操作日志
|
||
version_ids = [version.version_id for version in versions]
|
||
log_operation('BATCH_DELETE_VERSIONS', 'VERSION', None, {
|
||
'version_ids': version_ids,
|
||
'count': len(versions)
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': f'成功删除 {len(versions)} 个版本'
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
current_app.logger.error(f"批量删除版本失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '服务器内部错误'
|
||
}), 500
|
||
|
||
|
||
@api_bp.route('/versions/batch/status', methods=['PUT'])
|
||
@require_login
|
||
def batch_update_version_status():
|
||
"""批量更新版本状态(发布/取消发布)"""
|
||
try:
|
||
data = request.get_json()
|
||
if not data or 'version_ids' not in data or 'status' not in data:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '请求数据为空或缺少version_ids/status字段'
|
||
}), 400
|
||
|
||
version_ids = data['version_ids']
|
||
status = data['status']
|
||
|
||
if not isinstance(version_ids, list) or len(version_ids) == 0:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': 'version_ids必须是非空列表'
|
||
}), 400
|
||
|
||
if status not in [0, 1]:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': 'status必须是0(草稿)或1(已发布)'
|
||
}), 400
|
||
|
||
# 查找所有要更新的版本
|
||
versions = Version.query.filter(Version.version_id.in_(version_ids)).all()
|
||
if len(versions) != len(version_ids):
|
||
found_ids = [v.version_id for v in versions]
|
||
missing_ids = [vid for vid in version_ids if vid not in found_ids]
|
||
return jsonify({
|
||
'success': False,
|
||
'message': f'以下版本不存在: {", ".join(map(str, missing_ids))}'
|
||
}), 404
|
||
|
||
# 批量更新版本状态
|
||
for version in versions:
|
||
if status == 1:
|
||
version.publish()
|
||
else:
|
||
version.unpublish()
|
||
|
||
# 记录操作日志
|
||
version_ids = [version.version_id for version in versions]
|
||
log_operation('BATCH_UPDATE_VERSION_STATUS', 'VERSION', None, {
|
||
'version_ids': version_ids,
|
||
'status': status,
|
||
'count': len(versions)
|
||
})
|
||
|
||
status_name = '发布' if status == 1 else '取消发布'
|
||
return jsonify({
|
||
'success': True,
|
||
'message': f'成功{status_name} {len(versions)} 个版本'
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
current_app.logger.error(f"批量更新版本状态失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '服务器内部错误'
|
||
}), 500
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|