task-116: 后台管理新增/导入表单弹窗化 + 分组管理独立菜单与UI优化 + 轻量进度端点
Build Backend JAR / build (push) Has been cancelled
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:
@@ -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:
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
var guides = {
|
||||
users: { text: '先用筛选定位账号,再编辑角色和菜单权限;删除账号会要求二次确认。', steps: ['筛选账号', '编辑权限', '确认保存'] },
|
||||
columns: { text: '菜单会影响后台和软件端的可见范围。建议先填写名称、标识和页面路径,再设置上级菜单。', steps: ['新增或调整菜单', '设置层级', '检查权限'] },
|
||||
columns: { text: '菜单会影响后台和软件端的可见范围。先填写名称并选择对应页面,再设置上级菜单;顺序直接拖动列表左侧手柄调整。', steps: ['新增或调整菜单', '设置层级', '拖动排序'] },
|
||||
'dedupe-total-data': { text: '支持按关键词、用户、分组和国家筛选;导入前先确认所选分组,导出会按日期范围生成文件。', steps: ['选择分组', '筛选或导入', '核对并导出'] },
|
||||
'invalid-asin-data': { text: '维护不符合规则的 ASIN 或品牌。添加后可使用上方筛选快速回查。', steps: ['填写 ASIN/品牌', '选择分组', '保存并回查'] },
|
||||
'shop-keys': { text: '紫鸟令牌属于敏感配置。白名单状态可悬停查看检测详情,编辑前请先核对账号名称。', steps: ['新增或筛选密钥', '查看白名单状态', '编辑或删除'] },
|
||||
'shop-manage': { text: '店铺信息按分组管理。长商城名会自动缩略,悬停即可查看完整内容。', steps: ['选择分组', '维护店铺信息', '筛选核对结果'] },
|
||||
'skip-price-asin': { text: '最低价 ASIN 以店铺和国家为单位维护。每个国家可单独编辑 ASIN 与最低价。', steps: ['选择店铺和国家', '填写 ASIN/最低价', '保存或批量导入'] },
|
||||
'query-asin': { text: '查询 ASIN 按店铺和国家独立维护。空国家列可直接编辑,已有记录可修改或删除。', steps: ['选择店铺和国家', '维护各国 ASIN', '筛选或导出'] },
|
||||
'skip-price-asin': { text: '最低价 ASIN 以店铺为单位维护。列表里点击 ASIN 即可复制,点右侧「配置」可一次维护该店铺所有站点的 ASIN 与最低价。', steps: ['筛选店铺', '打开配置抽屉', '保存或批量导入'] },
|
||||
'query-asin': { text: '查询 ASIN 以店铺为单位维护。列表里点击 ASIN 即可复制,点右侧「配置」可一次维护该店铺所有站点的 ASIN。', steps: ['筛选店铺', '打开配置抽屉', '保存或导出'] },
|
||||
'product-categories': { text: '类目树支持展开查看层级。搜索、编辑和删除都在同一列表中完成。', steps: ['搜索类目', '展开层级', '新增或编辑'] },
|
||||
'image-video-tasks': { text: '可先使用筛选缩小任务范围,再查看任务状态、结果和权限范围。', steps: ['设置筛选', '查看任务结果', '按需处理任务'] },
|
||||
'shop-data-crawl-tasks': { text: '店铺数据任务按状态和时间筛选。批量操作前请核对已选任务。', steps: ['筛选任务', '检查状态', '执行批量操作'] },
|
||||
|
||||
+821
-363
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ class AdminInvalidAsinDataTest(unittest.TestCase):
|
||||
'pageSize': 15,
|
||||
},
|
||||
}
|
||||
with self.app.test_request_context('/api/admin/invalid-asin-data?page=1&page_size=15&group_id=3'):
|
||||
with self.app.test_request_context('/api/admin/invalid-asin-data?page=1&page_size=15&data_value=B01&brand=acme&group_id=3'):
|
||||
with patch.object(
|
||||
admin_api,
|
||||
'_ensure_backend_menu_access',
|
||||
@@ -58,6 +58,8 @@ class AdminInvalidAsinDataTest(unittest.TestCase):
|
||||
'page': 1,
|
||||
'pageSize': 15,
|
||||
'keyword': '',
|
||||
'dataValue': 'B01',
|
||||
'brand': 'acme',
|
||||
'groupId': 3,
|
||||
},
|
||||
)
|
||||
|
||||
+777
-598
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user