2026-01-29 09:27:56 +08:00
|
|
|
|
# 文件路径: app/api/v1/inbound/base.py
|
2026-02-25 09:55:25 +08:00
|
|
|
|
|
2026-03-17 11:56:04 +08:00
|
|
|
|
from flask import Blueprint, request, jsonify, send_file, g, current_app
|
2026-03-20 15:23:20 +08:00
|
|
|
|
from app.extensions import db, beijing_time
|
2026-01-28 08:54:11 +08:00
|
|
|
|
from app.services.inbound.base_service import MaterialBaseService
|
2026-03-10 17:16:57 +08:00
|
|
|
|
from app.utils.decorators import login_required, permission_required, audit_log
|
2026-03-11 17:10:07 +08:00
|
|
|
|
from app.models.base import MaterialBase, MaterialWarningSetting
|
2026-01-28 08:54:11 +08:00
|
|
|
|
import traceback
|
2026-02-25 09:55:25 +08:00
|
|
|
|
import datetime
|
2026-03-02 15:22:04 +08:00
|
|
|
|
import json
|
2026-01-28 08:54:11 +08:00
|
|
|
|
|
2026-02-06 10:16:37 +08:00
|
|
|
|
inbound_base_bp = Blueprint('stock_base', __name__)
|
2026-01-28 08:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 10:16:43 +08:00
|
|
|
|
# ==============================================================================
|
2026-02-27 10:20:09 +08:00
|
|
|
|
# 辅助函数:获取当前用户的完整权限列表(基于角色查询)
|
2026-02-27 10:16:43 +08:00
|
|
|
|
# ==============================================================================
|
2026-02-27 10:20:09 +08:00
|
|
|
|
def get_current_user_permissions():
|
2026-02-27 10:16:43 +08:00
|
|
|
|
"""
|
2026-02-27 10:20:09 +08:00
|
|
|
|
返回当前用户拥有的所有权限码列表(包括菜单和元素)
|
|
|
|
|
|
此函数根据角色查询数据库得到权限。
|
2026-02-27 10:16:43 +08:00
|
|
|
|
"""
|
|
|
|
|
|
from flask_jwt_extended import get_jwt
|
2026-02-27 10:20:09 +08:00
|
|
|
|
from app.services.auth_service import AuthService
|
2026-02-27 10:16:43 +08:00
|
|
|
|
claims = get_jwt()
|
|
|
|
|
|
user_role = claims.get('role')
|
2026-02-27 10:20:09 +08:00
|
|
|
|
if not user_role:
|
|
|
|
|
|
return []
|
2026-02-27 16:34:23 +08:00
|
|
|
|
# 超级管理员返回所有字段权限 (忽略大小写)
|
|
|
|
|
|
if user_role.upper() == 'SUPER_ADMIN':
|
2026-02-28 11:23:00 +08:00
|
|
|
|
# 返回通配符权限(供列表脱敏使用)以及所有具体权限(供导出脱敏使用)
|
|
|
|
|
|
return [
|
|
|
|
|
|
'material_list:*',
|
|
|
|
|
|
'material_list:id',
|
|
|
|
|
|
'material_list:companyName',
|
|
|
|
|
|
'material_list:name',
|
|
|
|
|
|
'material_list:commonName',
|
|
|
|
|
|
'material_list:category',
|
|
|
|
|
|
'material_list:type',
|
|
|
|
|
|
'material_list:spec',
|
|
|
|
|
|
'material_list:unit',
|
|
|
|
|
|
'material_list:inventoryCount',
|
|
|
|
|
|
'material_list:availableCount',
|
|
|
|
|
|
'material_list:files',
|
|
|
|
|
|
'material_list:isEnabled',
|
|
|
|
|
|
'material_list:operation'
|
|
|
|
|
|
]
|
2026-02-27 10:20:09 +08:00
|
|
|
|
perm_dict = AuthService.get_user_permissions(user_role)
|
|
|
|
|
|
# 合并菜单和元素权限
|
|
|
|
|
|
perms = perm_dict.get('menus', []) + perm_dict.get('elements', [])
|
|
|
|
|
|
return perms
|
2026-02-27 10:16:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 10:20:09 +08:00
|
|
|
|
def filter_item_by_permissions(item_dict, user_permissions):
|
2026-02-27 10:16:43 +08:00
|
|
|
|
"""
|
2026-02-27 10:20:09 +08:00
|
|
|
|
根据用户权限过滤 item 字典,无权限的字段值置为 None
|
2026-02-27 10:16:43 +08:00
|
|
|
|
"""
|
2026-02-27 16:34:23 +08:00
|
|
|
|
# 如果用户拥有通配符权限,则不过滤
|
|
|
|
|
|
if 'material_list:*' in user_permissions:
|
|
|
|
|
|
return item_dict
|
2026-02-27 10:16:43 +08:00
|
|
|
|
# 字段名到权限码的映射(与前端 permissionMap 保持一致)
|
|
|
|
|
|
field_to_perm = {
|
2026-02-27 10:43:32 +08:00
|
|
|
|
'id': 'material_list:id',
|
|
|
|
|
|
'companyName': 'material_list:companyName',
|
|
|
|
|
|
'name': 'material_list:name',
|
|
|
|
|
|
'commonName': 'material_list:commonName',
|
|
|
|
|
|
'category': 'material_list:category',
|
|
|
|
|
|
'type': 'material_list:type',
|
|
|
|
|
|
'spec': 'material_list:spec',
|
|
|
|
|
|
'unit': 'material_list:unit',
|
|
|
|
|
|
'inventoryCount': 'material_list:inventoryCount',
|
|
|
|
|
|
'availableCount': 'material_list:availableCount',
|
|
|
|
|
|
'generalManual': 'material_list:files',
|
|
|
|
|
|
'generalImage': 'material_list:files',
|
|
|
|
|
|
'isEnabled': 'material_list:isEnabled'
|
2026-02-27 10:16:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
for field, perm_code in field_to_perm.items():
|
2026-02-27 10:20:09 +08:00
|
|
|
|
if field in item_dict and perm_code not in user_permissions:
|
2026-02-27 10:16:43 +08:00
|
|
|
|
item_dict[field] = None
|
|
|
|
|
|
return item_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 1. 搜索接口 (GET /api/v1/inbound/base/search)
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/search', methods=['GET'])
|
2026-02-27 10:36:28 +08:00
|
|
|
|
@permission_required('material_list')
|
2026-01-29 09:27:56 +08:00
|
|
|
|
def search_base():
|
|
|
|
|
|
try:
|
|
|
|
|
|
keyword = request.args.get('keyword', '')
|
|
|
|
|
|
data = MaterialBaseService.search_material(keyword)
|
2026-02-27 10:16:43 +08:00
|
|
|
|
# 字段级脱敏
|
2026-02-27 10:20:09 +08:00
|
|
|
|
user_permissions = get_current_user_permissions()
|
|
|
|
|
|
filtered_data = [filter_item_by_permissions(item, user_permissions) for item in data]
|
2026-02-27 10:16:43 +08:00
|
|
|
|
return jsonify({"code": 200, "msg": "success", "data": filtered_data})
|
2026-01-29 09:27:56 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 2. 列表接口 (GET /api/v1/inbound/base/list)
|
|
|
|
|
|
# ==============================================================================
|
2026-01-28 08:54:11 +08:00
|
|
|
|
@inbound_base_bp.route('/list', methods=['GET'])
|
2026-02-27 10:36:28 +08:00
|
|
|
|
@permission_required('material_list')
|
2026-01-28 08:54:11 +08:00
|
|
|
|
def get_list():
|
|
|
|
|
|
try:
|
2026-02-25 09:55:25 +08:00
|
|
|
|
page = request.args.get('pageNum', 1, type=int)
|
2026-01-28 08:54:11 +08:00
|
|
|
|
limit = request.args.get('pageSize', 10, type=int)
|
|
|
|
|
|
|
2026-03-02 15:22:04 +08:00
|
|
|
|
# 解析高级筛选条件
|
|
|
|
|
|
advanced_filters_raw = request.args.get('advancedFilters', '[]')
|
|
|
|
|
|
try:
|
|
|
|
|
|
advanced_filters_list = json.loads(advanced_filters_raw)
|
|
|
|
|
|
except:
|
|
|
|
|
|
advanced_filters_list = []
|
|
|
|
|
|
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# 构造筛选条件
|
2026-01-28 08:54:11 +08:00
|
|
|
|
filters = {
|
2026-01-29 09:27:56 +08:00
|
|
|
|
'keyword': request.args.get('keyword', ''),
|
2026-02-25 09:55:25 +08:00
|
|
|
|
'company': request.args.get('company', ''),
|
2026-01-29 09:27:56 +08:00
|
|
|
|
'category': request.args.get('category', ''),
|
|
|
|
|
|
'type': request.args.get('type', ''),
|
2026-02-28 11:09:02 +08:00
|
|
|
|
'isEnabled': request.args.get('isEnabled', None),
|
|
|
|
|
|
'orderByColumn': request.args.get('orderByColumn', ''),
|
2026-03-02 15:22:04 +08:00
|
|
|
|
'isAsc': request.args.get('isAsc', None),
|
2026-03-11 13:11:16 +08:00
|
|
|
|
'advancedFilters': advanced_filters_list,
|
2026-03-11 13:28:11 +08:00
|
|
|
|
'enableWarningSort': request.args.get('enableWarningSort', 'false').lower() == 'true',
|
2026-03-11 13:37:52 +08:00
|
|
|
|
'has_stock': request.args.get('has_stock', ''),
|
|
|
|
|
|
'searchField': request.args.get('searchField', 'all')
|
2026-01-28 08:54:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 17:53:52 +08:00
|
|
|
|
user_permissions = get_current_user_permissions()
|
2026-03-11 17:21:31 +08:00
|
|
|
|
|
|
|
|
|
|
# 自动拦截:如果用户有预警查看权限,且当前没有按特定列手动排序,则强制开启预警智能排序
|
|
|
|
|
|
has_warning_perm = 'material_list:view_warning' in user_permissions
|
|
|
|
|
|
if has_warning_perm and not filters.get('orderByColumn'):
|
|
|
|
|
|
filters['enableWarningSort'] = True
|
|
|
|
|
|
|
2026-03-02 17:53:52 +08:00
|
|
|
|
result = MaterialBaseService.get_list(page, limit, filters, user_permissions)
|
2026-02-27 10:16:43 +08:00
|
|
|
|
# 字段级脱敏
|
2026-02-27 10:20:09 +08:00
|
|
|
|
user_permissions = get_current_user_permissions()
|
2026-02-27 10:16:43 +08:00
|
|
|
|
if result.get('items'):
|
2026-02-27 10:20:09 +08:00
|
|
|
|
result['items'] = [filter_item_by_permissions(item, user_permissions) for item in result['items']]
|
2026-01-29 09:27:56 +08:00
|
|
|
|
return jsonify({"code": 200, "msg": "success", "data": result})
|
2026-01-28 08:54:11 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
2026-02-11 13:12:05 +08:00
|
|
|
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ==============================================================================
|
2026-02-25 09:55:25 +08:00
|
|
|
|
# 2.1 选项接口 (GET /api/v1/inbound/base/options)
|
2026-02-11 13:12:05 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/options', methods=['GET'])
|
2026-02-27 10:36:28 +08:00
|
|
|
|
@permission_required('material_list')
|
2026-02-11 13:12:05 +08:00
|
|
|
|
def get_options():
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = MaterialBaseService.get_distinct_options()
|
|
|
|
|
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
2026-01-28 08:54:11 +08:00
|
|
|
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-25 09:55:25 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 2.2 导出接口 (GET /api/v1/inbound/base/export)
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/export', methods=['GET'])
|
2026-02-27 10:36:28 +08:00
|
|
|
|
@permission_required('material_list')
|
2026-02-25 09:55:25 +08:00
|
|
|
|
def export_data():
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取筛选条件
|
|
|
|
|
|
filters = {
|
|
|
|
|
|
'keyword': request.args.get('keyword', ''),
|
|
|
|
|
|
'company': request.args.get('company', ''),
|
|
|
|
|
|
'category': request.args.get('category', ''),
|
|
|
|
|
|
'type': request.args.get('type', ''),
|
|
|
|
|
|
'isEnabled': request.args.get('isEnabled', None)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 11:09:02 +08:00
|
|
|
|
# 获取当前用户权限
|
|
|
|
|
|
user_permissions = get_current_user_permissions()
|
|
|
|
|
|
|
|
|
|
|
|
# 生成 Excel 文件流(传入用户权限进行脱敏)
|
|
|
|
|
|
file_stream = MaterialBaseService.export_excel(filters, user_permissions)
|
2026-02-25 09:55:25 +08:00
|
|
|
|
|
|
|
|
|
|
# 生成文件名:库存统计+年月日+时分秒 (北京时间 UTC+8)
|
2026-03-20 15:23:20 +08:00
|
|
|
|
bj_time = beijing_time()
|
|
|
|
|
|
filename = f"库存统计_{bj_time.strftime('%Y%m%d_%H%M%S')}.xlsx"
|
2026-02-25 09:55:25 +08:00
|
|
|
|
|
|
|
|
|
|
# 发送文件
|
|
|
|
|
|
# 注意:download_name 仅在较新 Flask 版本有效,旧版本可能需要手动 header,
|
|
|
|
|
|
# 但通常浏览器下载名由前端 Blob 处理或 Content-Disposition 决定。
|
|
|
|
|
|
return send_file(
|
|
|
|
|
|
file_stream,
|
|
|
|
|
|
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
|
|
|
|
as_attachment=True,
|
|
|
|
|
|
download_name=filename
|
|
|
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return jsonify({"code": 500, "msg": f"导出失败: {str(e)}"}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 3. 新增接口 (POST /api/v1/inbound/base/)
|
|
|
|
|
|
# ==============================================================================
|
2026-01-28 08:54:11 +08:00
|
|
|
|
@inbound_base_bp.route('/', methods=['POST'])
|
2026-02-27 10:43:32 +08:00
|
|
|
|
@permission_required('material_list:operation')
|
2026-03-10 17:16:57 +08:00
|
|
|
|
@audit_log(
|
|
|
|
|
|
module='基础信息管理',
|
|
|
|
|
|
action='新增',
|
|
|
|
|
|
get_target_name_fn=lambda: request.get_json().get('name') if request.get_json() else None
|
|
|
|
|
|
)
|
2026-01-29 09:27:56 +08:00
|
|
|
|
def create():
|
2026-01-28 08:54:11 +08:00
|
|
|
|
try:
|
|
|
|
|
|
data = request.get_json()
|
|
|
|
|
|
if not data:
|
|
|
|
|
|
return jsonify({"code": 400, "msg": "No data provided"}), 400
|
|
|
|
|
|
|
2026-02-27 14:41:27 +08:00
|
|
|
|
# 获取当前用户权限
|
|
|
|
|
|
user_permissions = get_current_user_permissions()
|
|
|
|
|
|
# 字段到权限码的映射(与 filter_item_by_permissions 一致)
|
|
|
|
|
|
field_to_perm = {
|
|
|
|
|
|
'id': 'material_list:id',
|
|
|
|
|
|
'companyName': 'material_list:companyName',
|
|
|
|
|
|
'name': 'material_list:name',
|
|
|
|
|
|
'commonName': 'material_list:commonName',
|
|
|
|
|
|
'category': 'material_list:category',
|
|
|
|
|
|
'type': 'material_list:type',
|
|
|
|
|
|
'spec': 'material_list:spec',
|
|
|
|
|
|
'unit': 'material_list:unit',
|
|
|
|
|
|
'inventoryCount': 'material_list:inventoryCount',
|
|
|
|
|
|
'availableCount': 'material_list:availableCount',
|
|
|
|
|
|
'generalManual': 'material_list:files',
|
|
|
|
|
|
'generalImage': 'material_list:files',
|
|
|
|
|
|
'isEnabled': 'material_list:isEnabled'
|
|
|
|
|
|
}
|
|
|
|
|
|
# 过滤用户没有权限的字段
|
|
|
|
|
|
filtered_data = {}
|
2026-02-27 16:34:23 +08:00
|
|
|
|
# 如果拥有通配符权限,则不过滤
|
|
|
|
|
|
if 'material_list:*' in user_permissions:
|
|
|
|
|
|
filtered_data = data
|
|
|
|
|
|
else:
|
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
|
if key in field_to_perm:
|
|
|
|
|
|
perm_code = field_to_perm[key]
|
|
|
|
|
|
if perm_code in user_permissions:
|
|
|
|
|
|
filtered_data[key] = value
|
|
|
|
|
|
# 没有权限则跳过,不包含在 filtered_data 中
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 不在映射中的字段,默认允许(例如 visibilityLevel)
|
2026-02-27 14:41:27 +08:00
|
|
|
|
filtered_data[key] = value
|
|
|
|
|
|
|
|
|
|
|
|
MaterialBaseService.create_material(filtered_data)
|
2026-01-28 08:54:11 +08:00
|
|
|
|
return jsonify({"code": 200, "msg": "新增成功"})
|
2026-01-29 09:27:56 +08:00
|
|
|
|
except ValueError as e:
|
|
|
|
|
|
# 捕获业务逻辑验证错误 (如名称为空)
|
|
|
|
|
|
return jsonify({"code": 400, "msg": str(e)}), 400
|
2026-01-28 08:54:11 +08:00
|
|
|
|
except Exception as e:
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# 捕获系统错误
|
2026-01-28 08:54:11 +08:00
|
|
|
|
traceback.print_exc()
|
2026-01-29 09:27:56 +08:00
|
|
|
|
return jsonify({"code": 500, "msg": f"系统错误: {str(e)}"}), 500
|
2026-01-28 08:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 4. 修改接口 (PUT /api/v1/inbound/base/<id>)
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/<int:id>', methods=['PUT'])
|
2026-02-27 10:43:32 +08:00
|
|
|
|
@permission_required('material_list:operation')
|
2026-03-10 17:16:57 +08:00
|
|
|
|
@audit_log(
|
|
|
|
|
|
module='基础信息管理',
|
|
|
|
|
|
action='修改',
|
|
|
|
|
|
get_target_id_fn=lambda: request.view_args.get('id'),
|
|
|
|
|
|
get_target_name_fn=lambda: request.get_json().get('name') if request.get_json() else None
|
|
|
|
|
|
)
|
2026-01-29 09:27:56 +08:00
|
|
|
|
def update(id):
|
2026-01-28 08:54:11 +08:00
|
|
|
|
try:
|
|
|
|
|
|
data = request.get_json()
|
2026-02-27 14:41:27 +08:00
|
|
|
|
# 获取当前用户权限
|
|
|
|
|
|
user_permissions = get_current_user_permissions()
|
|
|
|
|
|
# 字段到权限码的映射(与 filter_item_by_permissions 一致)
|
|
|
|
|
|
field_to_perm = {
|
|
|
|
|
|
'id': 'material_list:id',
|
|
|
|
|
|
'companyName': 'material_list:companyName',
|
|
|
|
|
|
'name': 'material_list:name',
|
|
|
|
|
|
'commonName': 'material_list:commonName',
|
|
|
|
|
|
'category': 'material_list:category',
|
|
|
|
|
|
'type': 'material_list:type',
|
|
|
|
|
|
'spec': 'material_list:spec',
|
|
|
|
|
|
'unit': 'material_list:unit',
|
|
|
|
|
|
'inventoryCount': 'material_list:inventoryCount',
|
|
|
|
|
|
'availableCount': 'material_list:availableCount',
|
|
|
|
|
|
'generalManual': 'material_list:files',
|
|
|
|
|
|
'generalImage': 'material_list:files',
|
|
|
|
|
|
'isEnabled': 'material_list:isEnabled'
|
|
|
|
|
|
}
|
|
|
|
|
|
# 过滤用户没有权限的字段
|
|
|
|
|
|
filtered_data = {}
|
2026-02-27 16:34:23 +08:00
|
|
|
|
# 如果拥有通配符权限,则不过滤
|
|
|
|
|
|
if 'material_list:*' in user_permissions:
|
|
|
|
|
|
filtered_data = data
|
|
|
|
|
|
else:
|
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
|
if key in field_to_perm:
|
|
|
|
|
|
perm_code = field_to_perm[key]
|
|
|
|
|
|
if perm_code in user_permissions:
|
|
|
|
|
|
filtered_data[key] = value
|
|
|
|
|
|
# 没有权限则跳过,不包含在 filtered_data 中
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 不在映射中的字段,默认允许(例如 visibilityLevel)
|
2026-02-27 14:41:27 +08:00
|
|
|
|
filtered_data[key] = value
|
|
|
|
|
|
# 使用过滤后的数据调用服务
|
|
|
|
|
|
MaterialBaseService.update_material(id, filtered_data)
|
2026-01-29 09:27:56 +08:00
|
|
|
|
return jsonify({"code": 200, "msg": "修改成功"})
|
2026-01-28 08:54:11 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 09:27:56 +08:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 5. 删除接口 (DELETE /api/v1/inbound/base/<id>)
|
|
|
|
|
|
# ==============================================================================
|
2026-01-28 08:54:11 +08:00
|
|
|
|
@inbound_base_bp.route('/<int:id>', methods=['DELETE'])
|
2026-02-27 10:43:32 +08:00
|
|
|
|
@permission_required('material_list:operation')
|
2026-03-10 17:16:57 +08:00
|
|
|
|
@audit_log(
|
|
|
|
|
|
module='基础信息管理',
|
|
|
|
|
|
action='删除',
|
|
|
|
|
|
get_target_id_fn=lambda: request.view_args.get('id')
|
|
|
|
|
|
)
|
2026-01-29 09:27:56 +08:00
|
|
|
|
def delete(id):
|
2026-01-28 08:54:11 +08:00
|
|
|
|
try:
|
2026-03-20 15:47:13 +08:00
|
|
|
|
material_name = MaterialBaseService.delete_material(id)
|
|
|
|
|
|
return jsonify({"code": 200, "msg": "删除成功", "material_name": material_name})
|
2026-01-28 08:54:11 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
traceback.print_exc()
|
2026-02-27 10:16:43 +08:00
|
|
|
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
2026-03-11 13:11:16 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 2.5 批量设置预警 API (POST /api/v1/inbound/base/warning/batch-set)
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/warning/batch-set', methods=['POST'])
|
|
|
|
|
|
@permission_required('material_list:edit_warning')
|
|
|
|
|
|
def batch_set_warning():
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量设置物料预警配置
|
|
|
|
|
|
请求体格式: [
|
|
|
|
|
|
{"baseId": 1, "isEnabled": true, "yellowThreshold": 10, "redThreshold": 5},
|
|
|
|
|
|
{"baseId": 2, "isEnabled": false}
|
|
|
|
|
|
]
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = request.get_json()
|
|
|
|
|
|
if not isinstance(data, list):
|
|
|
|
|
|
return jsonify({"code": 400, "msg": "请求体必须为数组"})
|
|
|
|
|
|
|
|
|
|
|
|
updated_count = 0
|
|
|
|
|
|
created_count = 0
|
|
|
|
|
|
|
|
|
|
|
|
for item in data:
|
|
|
|
|
|
base_id = item.get('baseId')
|
|
|
|
|
|
if not base_id:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# 查找物料是否存在
|
|
|
|
|
|
material = MaterialBase.query.get(base_id)
|
|
|
|
|
|
if not material:
|
|
|
|
|
|
current_app.logger.warning(f"物料ID {base_id} 不存在,跳过")
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# 查找现有预警设置
|
|
|
|
|
|
warning = MaterialWarningSetting.query.filter_by(base_id=base_id).first()
|
|
|
|
|
|
|
|
|
|
|
|
if warning:
|
|
|
|
|
|
# 更新现有记录
|
|
|
|
|
|
if 'isEnabled' in item:
|
|
|
|
|
|
warning.is_enabled = bool(item['isEnabled'])
|
2026-03-11 17:06:19 +08:00
|
|
|
|
# 安全转换阈值,None 默认转为 0
|
|
|
|
|
|
yellow_val = item.get('yellowThreshold')
|
|
|
|
|
|
red_val = item.get('redThreshold')
|
|
|
|
|
|
warning.yellow_threshold = float(yellow_val) if yellow_val is not None else 0
|
|
|
|
|
|
warning.red_threshold = float(red_val) if red_val is not None else 0
|
2026-03-11 13:11:16 +08:00
|
|
|
|
updated_count += 1
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 创建新记录
|
2026-03-11 17:06:19 +08:00
|
|
|
|
yellow_val = item.get('yellowThreshold')
|
|
|
|
|
|
red_val = item.get('redThreshold')
|
2026-03-11 13:11:16 +08:00
|
|
|
|
warning = MaterialWarningSetting(
|
|
|
|
|
|
base_id=base_id,
|
|
|
|
|
|
is_enabled=item.get('isEnabled', False),
|
2026-03-11 17:06:19 +08:00
|
|
|
|
yellow_threshold=float(yellow_val) if yellow_val is not None else 0,
|
|
|
|
|
|
red_threshold=float(red_val) if red_val is not None else 0
|
2026-03-11 13:11:16 +08:00
|
|
|
|
)
|
|
|
|
|
|
db.session.add(warning)
|
|
|
|
|
|
created_count += 1
|
|
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
"code": 200,
|
|
|
|
|
|
"msg": "批量设置成功",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"created": created_count,
|
|
|
|
|
|
"updated": updated_count
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
db.session.rollback()
|
|
|
|
|
|
current_app.logger.error(f"批量设置预警失败: {str(e)}")
|
|
|
|
|
|
return jsonify({"code": 500, "msg": f"批量设置预警失败: {str(e)}"}), 500
|
2026-03-17 11:56:04 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 2.6 批量设置强制质检 API (POST /api/v1/inbound/base/batch-inspection)
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
@inbound_base_bp.route('/batch-inspection', methods=['POST'])
|
|
|
|
|
|
@permission_required('material_list:operation')
|
|
|
|
|
|
def batch_set_inspection():
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量设置物料强制质检标记
|
|
|
|
|
|
请求体格式: {
|
|
|
|
|
|
"ids": [1, 2, 3],
|
|
|
|
|
|
"isInspectionRequired": true
|
|
|
|
|
|
}
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = request.get_json()
|
|
|
|
|
|
if not data:
|
|
|
|
|
|
return jsonify({"code": 400, "msg": "No data provided"}), 400
|
|
|
|
|
|
|
|
|
|
|
|
ids = data.get('ids', [])
|
|
|
|
|
|
is_inspection_required = bool(data.get('isInspectionRequired', False))
|
|
|
|
|
|
|
|
|
|
|
|
if not ids:
|
|
|
|
|
|
return jsonify({"code": 400, "msg": "请选择要设置的物料"}), 400
|
|
|
|
|
|
|
|
|
|
|
|
updated_count = 0
|
|
|
|
|
|
for base_id in ids:
|
|
|
|
|
|
material = MaterialBase.query.get(base_id)
|
|
|
|
|
|
if material:
|
|
|
|
|
|
material.is_inspection_required = is_inspection_required
|
|
|
|
|
|
updated_count += 1
|
|
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
"code": 200,
|
|
|
|
|
|
"msg": f"批量设置成功,已更新 {updated_count} 条记录",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"updated": updated_count
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
db.session.rollback()
|
|
|
|
|
|
current_app.logger.error(f"批量设置强制质检失败: {str(e)}")
|
|
|
|
|
|
return jsonify({"code": 500, "msg": f"批量设置强制质检失败: {str(e)}"}), 500
|