@@ -1356,19 +1356,7 @@ _SHOP_DATA_CRAWL_ADMIN_COLUMNS = f"""
|
||||
t.task_no, t.status AS task_status, t.request_json, t.result_json,
|
||||
t.error_message AS task_error, t.created_at, t.updated_at, t.finished_at,
|
||||
{_SHOP_DATA_CRAWL_LATEST_TIME_SQL} AS latest_file_updated_at,
|
||||
u.username,
|
||||
(SELECT j.id FROM biz_task_file_job j
|
||||
WHERE j.module_type = 'SHOP_DATA_CRAWL' AND j.result_id = r.id
|
||||
AND j.job_type = 'ASSEMBLE_RESULT'
|
||||
ORDER BY j.id DESC LIMIT 1) AS file_job_id,
|
||||
(SELECT j.status FROM biz_task_file_job j
|
||||
WHERE j.module_type = 'SHOP_DATA_CRAWL' AND j.result_id = r.id
|
||||
AND j.job_type = 'ASSEMBLE_RESULT'
|
||||
ORDER BY j.id DESC LIMIT 1) AS file_status,
|
||||
(SELECT j.error_message FROM biz_task_file_job j
|
||||
WHERE j.module_type = 'SHOP_DATA_CRAWL' AND j.result_id = r.id
|
||||
AND j.job_type = 'ASSEMBLE_RESULT'
|
||||
ORDER BY j.id DESC LIMIT 1) AS file_error
|
||||
u.username
|
||||
"""
|
||||
|
||||
|
||||
@@ -1599,7 +1587,34 @@ def list_shop_data_crawl_tasks():
|
||||
'ORDER BY ranked.latest_file_updated_at DESC, ranked.result_id DESC',
|
||||
tuple(params + selected_shop_names),
|
||||
)
|
||||
for row in cur.fetchall():
|
||||
all_result_rows = cur.fetchall()
|
||||
|
||||
result_ids = [int(row['result_id']) for row in all_result_rows if row.get('result_id')]
|
||||
file_job_map = {}
|
||||
if result_ids:
|
||||
fj_placeholders = ','.join(['%s'] * len(result_ids))
|
||||
cur.execute(
|
||||
'SELECT fj.result_id, fj.id AS file_job_id, fj.status AS file_status, '
|
||||
'fj.error_message AS file_error '
|
||||
'FROM biz_task_file_job fj '
|
||||
f'INNER JOIN (SELECT result_id, MAX(id) AS max_id '
|
||||
f'FROM biz_task_file_job '
|
||||
f"WHERE module_type = 'SHOP_DATA_CRAWL' AND job_type = 'ASSEMBLE_RESULT' "
|
||||
f'AND result_id IN (' + fj_placeholders + ') '
|
||||
f'GROUP BY result_id) latest '
|
||||
'ON fj.id = latest.max_id',
|
||||
tuple(result_ids),
|
||||
)
|
||||
for fj_row in cur.fetchall():
|
||||
file_job_map[int(fj_row['result_id'])] = fj_row
|
||||
|
||||
for row in all_result_rows:
|
||||
rid = int(row.get('result_id') or 0)
|
||||
fj = file_job_map.get(rid)
|
||||
if fj:
|
||||
row['file_job_id'] = fj.get('file_job_id')
|
||||
row['file_status'] = fj.get('file_status')
|
||||
row['file_error'] = fj.get('file_error')
|
||||
shop_key = _shop_data_crawl_shop_key(row.get('shop_name'))
|
||||
result_rows_by_shop.setdefault(shop_key, []).append(row)
|
||||
finally:
|
||||
@@ -2997,7 +3012,7 @@ def export_dedupe_total_data():
|
||||
params=params,
|
||||
headers={'X-Internal-Token': _resolve_internal_token()},
|
||||
stream=True,
|
||||
timeout=60,
|
||||
timeout=(10, 1800),
|
||||
)
|
||||
except requests.RequestException:
|
||||
return jsonify({'success': False, 'error': 'backend-java 服务不可用'}), 502
|
||||
@@ -3362,6 +3377,7 @@ def _format_shop_manage_item(item):
|
||||
'group_name': item.get('groupName') or '',
|
||||
'shop_name': item.get('shopName') or '',
|
||||
'mall_name': item.get('mallName') or '',
|
||||
'zn_username': item.get('znUsername') or '',
|
||||
'account': item.get('account') or '',
|
||||
'password': item.get('passwordMasked') or '',
|
||||
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
|
||||
@@ -3493,7 +3509,11 @@ def get_shop_manage_credential(item_id):
|
||||
credential = credential_result.get('data') or {}
|
||||
if str(credential.get('id')) != str(item_id):
|
||||
return jsonify({'success': False, 'error': '店铺凭据不匹配'}), 409
|
||||
response = jsonify({'success': True, 'password': credential.get('password') or ''})
|
||||
response = jsonify({
|
||||
'success': True,
|
||||
'zn_username': credential.get('znUsername') or '',
|
||||
'password': credential.get('password') or '',
|
||||
})
|
||||
response.headers['Cache-Control'] = 'no-store'
|
||||
return response
|
||||
|
||||
@@ -3509,6 +3529,7 @@ def create_shop_manage():
|
||||
'groupId': data.get('group_id'),
|
||||
'shopName': (data.get('shop_name') or '').strip(),
|
||||
'mallName': (data.get('mall_name') or '').strip(),
|
||||
'znUsername': (data.get('zn_username') or '').strip(),
|
||||
'account': (data.get('account') or '').strip(),
|
||||
'password': (data.get('password') or '').strip(),
|
||||
'createdById': current_row.get('id') if current_row else None,
|
||||
@@ -3534,6 +3555,7 @@ def create_shop_manage():
|
||||
'group_name': item.get('groupName') or '',
|
||||
'shop_name': item.get('shopName') or '',
|
||||
'mall_name': item.get('mallName') or '',
|
||||
'zn_username': item.get('znUsername') or '',
|
||||
'account': item.get('account') or '',
|
||||
'password': item.get('password') or '',
|
||||
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
|
||||
@@ -3556,6 +3578,8 @@ def update_shop_manage(item_id):
|
||||
'account': (data.get('account') or '').strip(),
|
||||
'password': (data.get('password') or '').strip(),
|
||||
}
|
||||
if 'zn_username' in data:
|
||||
payload['znUsername'] = (data.get('zn_username') or '').strip()
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'PUT',
|
||||
f'/api/admin/shop-manages/{item_id}',
|
||||
@@ -3576,6 +3600,7 @@ def update_shop_manage(item_id):
|
||||
'group_name': item.get('groupName') or '',
|
||||
'shop_name': item.get('shopName') or '',
|
||||
'mall_name': item.get('mallName') or '',
|
||||
'zn_username': item.get('znUsername') or '',
|
||||
'account': item.get('account') or '',
|
||||
'password': item.get('password') or '',
|
||||
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
|
||||
|
||||
+42
-7
@@ -2245,7 +2245,27 @@
|
||||
});
|
||||
}
|
||||
document.getElementById('btnSearchDedupeTotalData').onclick = function () { loadDedupeTotalData(1); };
|
||||
document.getElementById('btnExportDedupeTotalData').onclick = function () {
|
||||
var dedupeTotalDataExportButton = document.getElementById('btnExportDedupeTotalData');
|
||||
var dedupeTotalDataExportWait = document.getElementById('dedupeTotalDataExportWait');
|
||||
var dedupeTotalDataExportWaitSeconds = document.getElementById('dedupeTotalDataExportWaitSeconds');
|
||||
var dedupeTotalDataExportWaitTimer = null;
|
||||
function showDedupeTotalDataExportWait() {
|
||||
var startedAt = Date.now();
|
||||
dedupeTotalDataExportWaitSeconds.textContent = '0';
|
||||
dedupeTotalDataExportWait.classList.add('show');
|
||||
dedupeTotalDataExportWait.setAttribute('aria-hidden', 'false');
|
||||
dedupeTotalDataExportWaitTimer = setInterval(function () {
|
||||
dedupeTotalDataExportWaitSeconds.textContent = String(Math.floor((Date.now() - startedAt) / 1000));
|
||||
}, 1000);
|
||||
}
|
||||
function hideDedupeTotalDataExportWait() {
|
||||
clearInterval(dedupeTotalDataExportWaitTimer);
|
||||
dedupeTotalDataExportWaitTimer = null;
|
||||
dedupeTotalDataExportWait.classList.remove('show');
|
||||
dedupeTotalDataExportWait.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
dedupeTotalDataExportButton.onclick = function () {
|
||||
if (dedupeTotalDataExportButton.disabled) return;
|
||||
var username = (document.getElementById('searchDedupeTotalDataUsername').value || '').trim();
|
||||
var groupId = (document.getElementById('dedupeTotalDataGroupFilterId').value || '').trim();
|
||||
var dateRange = getDedupeTotalDataDateRange();
|
||||
@@ -2255,6 +2275,11 @@
|
||||
if (groupId) params.push('group_id=' + encodeURIComponent(groupId));
|
||||
if (dateRange.startDate) params.push('start_date=' + encodeURIComponent(dateRange.startDate));
|
||||
if (dateRange.endDate) params.push('end_date=' + encodeURIComponent(dateRange.endDate));
|
||||
var originalButtonText = dedupeTotalDataExportButton.textContent;
|
||||
dedupeTotalDataExportButton.disabled = true;
|
||||
dedupeTotalDataExportButton.setAttribute('aria-busy', 'true');
|
||||
dedupeTotalDataExportButton.textContent = '导出中...';
|
||||
showDedupeTotalDataExportWait();
|
||||
fetch('/api/admin/dedupe-total-data/export' + (params.length ? ('?' + params.join('&')) : ''))
|
||||
.then(function (response) {
|
||||
var contentType = response.headers.get('content-type') || '';
|
||||
@@ -2278,6 +2303,12 @@
|
||||
})
|
||||
.catch(function (err) {
|
||||
alert((err && err.message) || '导出失败');
|
||||
})
|
||||
.finally(function () {
|
||||
dedupeTotalDataExportButton.disabled = false;
|
||||
dedupeTotalDataExportButton.removeAttribute('aria-busy');
|
||||
dedupeTotalDataExportButton.textContent = originalButtonText;
|
||||
hideDedupeTotalDataExportWait();
|
||||
});
|
||||
};
|
||||
var dedupeImportPollTimer = null;
|
||||
@@ -2907,16 +2938,16 @@
|
||||
.then(function (res) {
|
||||
var tbody = document.getElementById('shopManageListBody');
|
||||
if (!res.success) {
|
||||
tbody.innerHTML = '<tr><td colspan="9" class="empty-tip">加载失败: ' + (res.error || '') + '</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="10" class="empty-tip">加载失败: ' + (res.error || '') + '</td></tr>';
|
||||
return;
|
||||
}
|
||||
var items = res.items || [];
|
||||
if (items.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="9" class="empty-tip">暂无店铺</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="10" class="empty-tip">暂无店铺</td></tr>';
|
||||
} else {
|
||||
tbody.innerHTML = items.map(function (item, index) {
|
||||
var rowNo = (shopManagePage - 1) * shopManagePageSize + index + 1;
|
||||
return '<tr><td>' + rowNo + '</td><td>' + (item.group_name || '') + '</td><td>' + (item.shop_name || '') + '</td><td>' + (item.mall_name || '') + '</td><td>' + (item.account || '') + '</td><td>' + renderShopPasswordCell(item) + '</td><td>' + (item.created_at || '') + '</td><td>' + (item.updated_at || '') + '</td><td>' +
|
||||
return '<tr><td>' + rowNo + '</td><td>' + (item.group_name || '') + '</td><td>' + (item.shop_name || '') + '</td><td>' + (item.mall_name || '') + '</td><td>' + (item.zn_username || '') + '</td><td>' + (item.account || '') + '</td><td>' + renderShopPasswordCell(item) + '</td><td>' + (item.created_at || '') + '</td><td>' + (item.updated_at || '') + '</td><td>' +
|
||||
'<button class="btn btn-sm" data-shop-manage-edit="' + item.id + '" data-shop-manage="' + (JSON.stringify(item).replace(/"/g, '"')) + '">编辑</button> ' +
|
||||
'<button class="btn btn-sm btn-danger" data-shop-manage-delete="' + item.id + '" data-shop-manage-name="' + (item.shop_name || '').replace(/"/g, '"') + '">删除</button>' +
|
||||
'</td></tr>';
|
||||
@@ -2926,7 +2957,7 @@
|
||||
bindShopManageActions();
|
||||
})
|
||||
.catch(function () {
|
||||
document.getElementById('shopManageListBody').innerHTML = '<tr><td colspan="9" class="empty-tip">请求失败</td></tr>';
|
||||
document.getElementById('shopManageListBody').innerHTML = '<tr><td colspan="10" class="empty-tip">请求失败</td></tr>';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2974,6 +3005,7 @@
|
||||
document.getElementById('editShopManageId').value = item.id || '';
|
||||
document.getElementById('editShopManageShopName').value = item.shop_name || '';
|
||||
document.getElementById('editShopManageMallName').value = item.mall_name || '';
|
||||
document.getElementById('editShopManageZnUsername').value = item.zn_username || '';
|
||||
document.getElementById('editShopManageAccount').value = item.account || '';
|
||||
document.getElementById('editShopManagePassword').value = item.password || '';
|
||||
document.getElementById('msgEditShopManage').textContent = '';
|
||||
@@ -3367,6 +3399,7 @@
|
||||
var groupId = (document.getElementById('shopManageGroupSelect').value || '').trim();
|
||||
var shopName = (document.getElementById('shopManageShopName').value || '').trim();
|
||||
var mallName = (document.getElementById('shopManageMallName').value || '').trim();
|
||||
var znUsername = (document.getElementById('shopManageZnUsername').value || '').trim();
|
||||
var account = (document.getElementById('shopManageAccount').value || '').trim();
|
||||
var password = (document.getElementById('shopManagePassword').value || '').trim();
|
||||
var msgEl = document.getElementById('msgShopManage');
|
||||
@@ -3380,7 +3413,7 @@
|
||||
fetch('/api/admin/shop-manage', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ group_id: Number(groupId), shop_name: shopName, mall_name: mallName, account: account, password: password })
|
||||
body: JSON.stringify({ group_id: Number(groupId), shop_name: shopName, mall_name: mallName, zn_username: znUsername, account: account, password: password })
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
@@ -3388,6 +3421,7 @@
|
||||
document.getElementById('shopManageGroupSelect').value = '';
|
||||
document.getElementById('shopManageShopName').value = '';
|
||||
document.getElementById('shopManageMallName').value = '';
|
||||
document.getElementById('shopManageZnUsername').value = '';
|
||||
document.getElementById('shopManageAccount').value = '';
|
||||
document.getElementById('shopManagePassword').value = '';
|
||||
msgEl.textContent = res.msg || '创建成功';
|
||||
@@ -3409,6 +3443,7 @@
|
||||
var groupId = (document.getElementById('editShopManageGroupSelect').value || '').trim();
|
||||
var shopName = (document.getElementById('editShopManageShopName').value || '').trim();
|
||||
var mallName = (document.getElementById('editShopManageMallName').value || '').trim();
|
||||
var znUsername = (document.getElementById('editShopManageZnUsername').value || '').trim();
|
||||
var account = (document.getElementById('editShopManageAccount').value || '').trim();
|
||||
var password = (document.getElementById('editShopManagePassword').value || '').trim();
|
||||
var msgEl = document.getElementById('msgEditShopManage');
|
||||
@@ -3422,7 +3457,7 @@
|
||||
fetch('/api/admin/shop-manage/' + itemId, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ group_id: Number(groupId), shop_name: shopName, mall_name: mallName, account: account, password: password })
|
||||
body: JSON.stringify({ group_id: Number(groupId), shop_name: shopName, mall_name: mallName, zn_username: znUsername, account: account, password: password })
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
|
||||
@@ -152,6 +152,7 @@ class AdminDedupeTotalDataTest(unittest.TestCase):
|
||||
})
|
||||
self.assertEqual(session.kwargs['headers'], {'X-Internal-Token': 'token'})
|
||||
self.assertTrue(session.kwargs['stream'])
|
||||
self.assertEqual(session.kwargs['timeout'], (10, 1800))
|
||||
|
||||
def test_import_requires_group(self):
|
||||
with self.app.test_request_context(
|
||||
|
||||
@@ -291,6 +291,52 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dedupe-export-wait-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 3100;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
background: rgba(17, 24, 39, 0.38);
|
||||
}
|
||||
|
||||
.dedupe-export-wait-mask.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dedupe-export-wait {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
width: min(440px, calc(100vw - 40px));
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.dedupe-export-wait .request-spinner {
|
||||
flex: 0 0 auto;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.dedupe-export-wait-title {
|
||||
margin-bottom: 4px;
|
||||
color: #1f2937;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dedupe-export-wait-detail {
|
||||
color: #687386;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.request-spinner {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@@ -1721,6 +1767,18 @@
|
||||
<span class="request-spinner"></span>
|
||||
<span>请求处理中...</span>
|
||||
</div>
|
||||
<div class="dedupe-export-wait-mask" id="dedupeTotalDataExportWait" role="dialog" aria-modal="true"
|
||||
aria-labelledby="dedupeTotalDataExportWaitTitle" aria-hidden="true">
|
||||
<div class="dedupe-export-wait">
|
||||
<span class="request-spinner" aria-hidden="true"></span>
|
||||
<div>
|
||||
<div class="dedupe-export-wait-title" id="dedupeTotalDataExportWaitTitle">正在生成导出文件</div>
|
||||
<div class="dedupe-export-wait-detail">
|
||||
数据量较大,请耐心等待并保持页面打开。已等待 <span id="dedupeTotalDataExportWaitSeconds">0</span> 秒
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1>管理后台</h1>
|
||||
|
||||
<div class="admin-user-box" style="position:absolute;top:24px;right:24px;z-index:10;">
|
||||
@@ -2188,6 +2246,10 @@
|
||||
<label>店铺商城名</label>
|
||||
<input type="text" id="shopManageMallName" placeholder="请输入店铺商城名">
|
||||
</div>
|
||||
<div class="form-group" style="min-width:180px;">
|
||||
<label>自动化账号</label>
|
||||
<input type="text" id="shopManageZnUsername" maxlength="128" placeholder="请输入自动化账号(可选)">
|
||||
</div>
|
||||
<div class="form-group" style="min-width:180px;">
|
||||
<label>账号</label>
|
||||
<input type="text" id="shopManageAccount" placeholder="请输入账号">
|
||||
@@ -2222,6 +2284,7 @@
|
||||
<th>分组</th>
|
||||
<th>店铺名</th>
|
||||
<th>店铺商城名</th>
|
||||
<th>自动化账号</th>
|
||||
<th>账号</th>
|
||||
<th>密码</th>
|
||||
<th>创建时间</th>
|
||||
@@ -3003,6 +3066,10 @@
|
||||
<label>店铺商城名</label>
|
||||
<input type="text" id="editShopManageMallName" placeholder="店铺商城名">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>自动化账号</label>
|
||||
<input type="text" id="editShopManageZnUsername" maxlength="128" placeholder="自动化账号(可选)">
|
||||
</div>
|
||||
<div class=" form-group">
|
||||
<label>账号</label>
|
||||
<input type="text" id="editShopManageAccount" placeholder="账号">
|
||||
@@ -3108,7 +3175,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/admin.js?v=shop-data-task-admin-2"></script>
|
||||
<script src="/static/admin.js?v=dedupe-export-wait-1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user