task-116: 后台管理新增/导入表单弹窗化 + 分组管理独立菜单与UI优化 + 轻量进度端点
Build Backend JAR / build (push) Has been cancelled

- 后台管理页(admin)所有面板的新增/导入表单改为弹窗操作,原有字段 ID 全部保留、提交逻辑不变;导入删除入口不再触发二次确认拦截
- 分组管理升级为独立菜单(V102 + schema initializer),移除 5 个面板内的管理分组按钮;分组列表改为蓝白主题、增加权限分组横幅
- Python 侧 group-manage 权限守卫(_ensure_backend_menu_access 补充 group-manage)
- 引入 V101(biz_task_file_job 复合索引)+ 新增 TaskProgressLight/TaskFileJob 轻量端点与进度聚合作
- 前端 progress-light / page-separated-loads / dispatch-guard 共享模块及单元测试
This commit is contained in:
2026-09-01 12:54:13 +08:00
parent 759f0b15d8
commit fa5a59e5cd
139 changed files with 10540 additions and 8834 deletions
+48 -4
View File
@@ -64,6 +64,11 @@ ADMIN_MENU_ACCESS_CONFIG = {
'route_path': 'dedupe-total-data',
'error': '无权访问数据去重汇总数据',
},
'group-manage': {
'column_key': 'admin_group_manage',
'route_path': 'group-manage',
'error': '无权访问分组管理模块',
},
'shop-manage': {
'column_key': 'admin_shop_manage',
'route_path': 'shop-manage',
@@ -1523,6 +1528,7 @@ def list_shop_data_crawl_tasks():
page_size = min(100, max(10, int(request.args.get('page_size', 20))))
shop_name = (request.args.get('shop_name') or request.args.get('shop') or '').strip()
group_name = (request.args.get('group_name') or request.args.get('group') or '').strip()
country = (request.args.get('country') or '').strip().upper()
created_from = _parse_admin_datetime_arg('created_from')
created_to = _parse_admin_datetime_arg('created_to')
@@ -1544,6 +1550,16 @@ def list_shop_data_crawl_tasks():
"AND COALESCE(NULLIF(g.group_name, ''), NULLIF(sm.group_name, '')) LIKE %s)"
)
params.append('%' + group_name + '%')
if country:
if country not in ('DE', 'UK', 'FR', 'IT', 'ES'):
raise ValueError('不支持的国家代码: ' + country)
conditions.append(
'JSON_CONTAINS('
'COALESCE(df.country_codes_json, '
'JSON_EXTRACT(t.request_json, \'$.countryCodes\'), '
'JSON_EXTRACT(t.request_json, \'$.country_codes\'), \'[]\'), %s)'
)
params.append('"' + country + '"')
if created_from:
conditions.append('t.created_at >= %s')
params.append(created_from)
@@ -3352,12 +3368,16 @@ def list_invalid_asin_data():
page = max(1, int(request.args.get('page', 1)))
page_size = min(100, max(1, int(request.args.get('page_size', 15))))
keyword = (request.args.get('keyword') or '').strip()
data_value = (request.args.get('data_value') or '').strip()
brand = (request.args.get('brand') or '').strip()
group_id_raw = (request.args.get('group_id') or '').strip()
group_id = int(group_id_raw) if group_id_raw.isdigit() and int(group_id_raw) > 0 else None
params = {
'page': page,
'pageSize': page_size,
'keyword': keyword,
'dataValue': data_value,
'brand': brand,
}
if group_id is not None:
params['groupId'] = group_id
@@ -3766,7 +3786,7 @@ def delete_shop_manage(item_id):
@login_required
def list_shop_manage_groups():
role, current_row, denied = _ensure_backend_menu_access(
'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
'group-manage', 'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
)
if denied:
return denied
@@ -3788,7 +3808,7 @@ def list_shop_manage_groups():
@login_required
def create_shop_manage_group():
role, current_row, denied = _ensure_backend_menu_access(
'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
'group-manage', 'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
)
if denied:
return denied
@@ -3835,7 +3855,7 @@ def create_shop_manage_group():
@login_required
def update_shop_manage_group(item_id):
role, current_row, denied = _ensure_backend_menu_access(
'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
'group-manage', 'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
)
if denied:
return denied
@@ -3888,6 +3908,9 @@ def list_skip_price_asins():
group_id_raw = (request.args.get('group_id') or '').strip()
shop_name = (request.args.get('shop_name') or '').strip()
asin = (request.args.get('asin') or '').strip()
country = (request.args.get('country') or '').strip()
minimum_price_from = (request.args.get('minimum_price_from') or '').strip()
minimum_price_to = (request.args.get('minimum_price_to') or '').strip()
params = {'page': page, 'page_size': page_size, 'pageSize': page_size}
params.update(_skip_price_asin_auth_params(role, current_row))
if group_id_raw:
@@ -3903,6 +3926,12 @@ def list_skip_price_asins():
params['shopName'] = shop_name
if asin:
params['asin'] = asin
if country:
params['country'] = country
if minimum_price_from:
params['minimum_price_from'] = minimum_price_from
if minimum_price_to:
params['minimum_price_to'] = minimum_price_to
result, error_response, status = _proxy_backend_java(
'GET',
'/api/admin/skip-price-asins',
@@ -3962,6 +3991,9 @@ def export_skip_price_asins():
group_id_raw = (request.args.get('group_id') or request.args.get('groupId') or '').strip()
shop_name = (request.args.get('shop_name') or request.args.get('shopName') or '').strip()
asin = (request.args.get('asin') or '').strip()
country = (request.args.get('country') or '').strip()
minimum_price_from = (request.args.get('minimum_price_from') or '').strip()
minimum_price_to = (request.args.get('minimum_price_to') or '').strip()
if group_id_raw:
params['group_id'] = group_id_raw
params['groupId'] = group_id_raw
@@ -3970,6 +4002,12 @@ def export_skip_price_asins():
params['shopName'] = shop_name
if asin:
params['asin'] = asin
if country:
params['country'] = country
if minimum_price_from:
params['minimum_price_from'] = minimum_price_from
if minimum_price_to:
params['minimum_price_to'] = minimum_price_to
url = f"{backend_java_base_url}/api/admin/skip-price-asins/export"
try:
@@ -4082,7 +4120,7 @@ def delete_skip_price_asin_country(item_id, country):
@login_required
def delete_shop_manage_group(item_id):
role, current_row, denied = _ensure_backend_menu_access(
'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
'group-manage', 'shop-manage', 'skip-price-asin', 'query-asin', 'dedupe-total-data', 'invalid-asin-data'
)
if denied:
return denied
@@ -4276,6 +4314,7 @@ def list_query_asins():
group_id_raw = (request.args.get('group_id') or '').strip()
shop_name = (request.args.get('shop_name') or '').strip()
asin = (request.args.get('asin') or '').strip()
country = (request.args.get('country') or '').strip()
params = {'page': page, 'pageSize': page_size}
if current_row and current_row.get('id'):
params['operatorId'] = current_row.get('id')
@@ -4291,6 +4330,8 @@ def list_query_asins():
params['shopName'] = shop_name
if asin:
params['asin'] = asin
if country:
params['country'] = country
result, error_response, status = _proxy_backend_java(
'GET',
'/api/admin/query-asins',
@@ -4321,12 +4362,15 @@ def export_query_asins():
group_id_raw = (request.args.get('group_id') or request.args.get('groupId') or '').strip()
shop_name = (request.args.get('shop_name') or request.args.get('shopName') or '').strip()
asin = (request.args.get('asin') or '').strip()
country = (request.args.get('country') or '').strip()
if group_id_raw:
params['groupId'] = group_id_raw
if shop_name:
params['shopName'] = shop_name
if asin:
params['asin'] = asin
if country:
params['country'] = country
url = f"{backend_java_base_url}/api/admin/query-asins/export"
try: