Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
A
aidso-data
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Yaowentong
aidso-data
Commits
27ac512a
Commit
27ac512a
authored
Jul 29, 2026
by
Yaowentong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
后台健全
parent
fc2c7163
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
4030 additions
and
422 deletions
+4030
-422
feishu_snipaste_v3.py
aidso_geo/core/feishu_snipaste_v3.py
+191
-118
interface.py
aidso_geo/core/interface.py
+6
-1
meituan_process.py
aidso_geo/core/meituan_process.py
+3
-2
dashboard.py
aidso_geo/core/routes/dashboard.py
+454
-20
third_interface.py
aidso_geo/core/routes/third_interface.py
+49
-3
aidso_logo.png
aidso_geo/core/static/dashboard/aidso_logo.png
+0
-0
dashboard_login.css
aidso_geo/core/static/dashboard/dashboard_login.css
+324
-0
queue_monitor.css
aidso_geo/core/static/dashboard/queue_monitor.css
+1803
-121
queue_monitor.js
aidso_geo/core/static/dashboard/queue_monitor.js
+821
-72
dashboard_login.html
aidso_geo/core/templates/dashboard_login.html
+76
-0
queue_monitor.html
aidso_geo/core/templates/queue_monitor.html
+133
-12
douyinai_data_process.py
aidso_geo/models/douyinai_data_process.py
+28
-3
qianwen_android_data_process.py
aidso_geo/models/qianwen_android_data_process.py
+140
-65
spider_save_tos.py
aidso_geo/models/spider_save_tos.py
+2
-5
No files found.
aidso_geo/core/feishu_snipaste_v3.py
View file @
27ac512a
...
@@ -5,7 +5,6 @@ from __future__ import annotations
...
@@ -5,7 +5,6 @@ from __future__ import annotations
import
json
import
json
import
os
import
os
import
sys
import
sys
import
traceback
import
uuid
import
uuid
from
datetime
import
datetime
from
datetime
import
datetime
import
time
import
time
...
@@ -15,7 +14,6 @@ from loguru import logger
...
@@ -15,7 +14,6 @@ from loguru import logger
from
openpyxl
import
Workbook
from
openpyxl
import
Workbook
from
aidso_geo.models
import
spider_save_tos
from
aidso_geo.models
import
spider_save_tos
from
aidso_geo.utils.tos_utils
import
get_string_from_tos
POLL_SECONDS
=
120
POLL_SECONDS
=
120
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))))
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))))
sys
.
path
.
append
(
BASE_DIR
)
sys
.
path
.
append
(
BASE_DIR
)
...
@@ -487,7 +485,6 @@ def doubao_process_original_data(file_path, original_content):
...
@@ -487,7 +485,6 @@ def doubao_process_original_data(file_path, original_content):
is_think
=
False
is_think
=
False
rich_media_block
=
[]
rich_media_block
=
[]
think_bool
=
False
think_bool
=
False
response_bool
=
False
file_path_result
=
os
.
path
.
dirname
(
file_path
)
file_path_result
=
os
.
path
.
dirname
(
file_path
)
content_list
=
original_content
.
split
(
"
\n
"
)
content_list
=
original_content
.
split
(
"
\n
"
)
...
@@ -701,79 +698,6 @@ def wait_mt_tasks_done(
...
@@ -701,79 +698,6 @@ def wait_mt_tasks_done(
time
.
sleep
(
POLL_SECONDS
)
time
.
sleep
(
POLL_SECONDS
)
def
get_task_content_with_retries
(
row
:
dict
,
file
:
str
,
redis_key
:
str
,
max_retries
:
int
=
3
,
):
req_id
=
row
.
get
(
"reqId"
)
redis_pt
=
normalize_pt
(
row
.
get
(
"pt"
))[:
8
]
last_error
=
None
for
retry_index
in
range
(
max_retries
+
1
):
try
:
raw
=
tos_utils
.
get_string_from_tos
(
file
)
except
Exception
as
exc
:
last_error
=
exc
raw
=
None
if
raw
:
try
:
content
=
json
.
loads
(
raw
)
.
get
(
"content"
)
except
(
TypeError
,
json
.
JSONDecodeError
)
as
exc
:
last_error
=
exc
content
=
None
if
content
:
return
raw
,
content
if
retry_index
>=
max_retries
:
break
retry_value
=
json
.
dumps
(
{
"reqId"
:
req_id
,
"prompt"
:
row
.
get
(
"prompt"
),
"pt"
:
redis_pt
,
},
ensure_ascii
=
False
,
)
redis_client
=
init_redis
()
if
redis_client
is
None
:
raise
TaskResultUnavailableError
(
"Redis 初始化失败"
)
try
:
redis_client
.
rpush
(
redis_key
,
retry_value
)
except
Exception
as
exc
:
raise
TaskResultUnavailableError
(
f
"TOS 结果缺失任务重发 Redis 失败: "
f
"key={redis_key}, reqId={req_id}"
)
from
exc
logger
.
warning
(
f
"[pt={row.get('pt')} count={row.get('count')} "
f
"reqId={req_id}] TOS 结果缺失或正文为空,"
f
"开始第 {retry_index + 1}/{max_retries} 次重试"
)
try
:
wait_mt_tasks_done
(
redis_key
,
pt
=
str
(
row
.
get
(
"pt"
)
or
""
),
count_label
=
str
(
row
.
get
(
"count"
)
or
""
),
)
except
Exception
as
exc
:
raise
TaskResultUnavailableError
(
f
"TOS 结果缺失任务等待 Redis 失败: "
f
"key={redis_key}, reqId={req_id}"
)
from
exc
time
.
sleep
(
60
)
error
=
TaskResultUnavailableError
(
f
"TOS 结果重试 {max_retries} 次后仍不可用: "
f
"reqId={req_id}, file={file}"
)
if
last_error
is
not
None
:
raise
error
from
last_error
raise
error
def
process_rounds_to_result
(
def
process_rounds_to_result
(
pt
:
str
,
pt
:
str
,
start_count
:
int
,
start_count
:
int
,
...
@@ -941,11 +865,26 @@ def process_rounds_to_result(
...
@@ -941,11 +865,26 @@ def process_rounds_to_result(
else
:
else
:
keywords
=
[]
keywords
=
[]
raw
,
content
=
get_task_content_with_retries
(
try
:
row
=
row
,
raw
=
tos_utils
.
get_string_from_tos
(
file
)
file
=
file
,
except
Exception
as
exc
:
redis_key
=
redis_key
,
raise
TaskResultUnavailableError
(
)
f
"TOS 结果读取失败: reqId={req_id}, file={file}"
)
from
exc
if
not
raw
:
raise
TaskResultUnavailableError
(
f
"TOS 结果不存在: reqId={req_id}, file={file}"
)
try
:
content
=
json
.
loads
(
raw
)
.
get
(
"content"
)
except
(
TypeError
,
json
.
JSONDecodeError
)
as
exc
:
raise
TaskResultUnavailableError
(
f
"TOS 结果格式异常: reqId={req_id}, file={file}"
)
from
exc
if
not
content
:
raise
TaskResultUnavailableError
(
f
"TOS 正文为空: reqId={req_id}, file={file}"
)
(
(
_
,
_
,
...
@@ -998,16 +937,15 @@ def process_rounds_to_result(
...
@@ -998,16 +937,15 @@ def process_rounds_to_result(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"[pt={pt} count={start_count}-{end_count}] "
f
"处理 {file} 失败: {exc}"
f
"处理 {file} 失败: {exc}"
)
)
if
isinstance
(
if
isinstance
(
exc
,
TaskResultUnavailableError
):
exc
,
raise
(
ResultWriteError
,
TaskResultUnavailableError
),
if
isinstance
(
exc
,
ResultWriteError
):
):
raise
raise
return
None
return
None
def
parse_result_batch
(
rows
,
batch_label
):
def
parse_result_batch
(
rows
,
batch_label
):
if
not
rows
:
if
not
rows
:
return
[]
return
[]
,
[]
worker_count
=
min
(
20
,
len
(
rows
))
worker_count
=
min
(
20
,
len
(
rows
))
logger
.
info
(
logger
.
info
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"[pt={pt} count={start_count}-{end_count}] "
...
@@ -1015,6 +953,8 @@ def process_rounds_to_result(
...
@@ -1015,6 +953,8 @@ def process_rounds_to_result(
f
"workers={worker_count}, tasks={len(rows)}"
f
"workers={worker_count}, tasks={len(rows)}"
)
)
parsed_results
=
[]
parsed_results
=
[]
missing_rows
=
[]
completed_count
=
0
with
ThreadPoolExecutor
(
max_workers
=
worker_count
)
as
executor
:
with
ThreadPoolExecutor
(
max_workers
=
worker_count
)
as
executor
:
future_to_row
=
{
future_to_row
=
{
executor
.
submit
(
parse_single_result
,
row
):
row
executor
.
submit
(
parse_single_result
,
row
):
row
...
@@ -1024,6 +964,9 @@ def process_rounds_to_result(
...
@@ -1024,6 +964,9 @@ def process_rounds_to_result(
row
=
future_to_row
[
future
]
row
=
future_to_row
[
future
]
try
:
try
:
result_item
=
future
.
result
()
result_item
=
future
.
result
()
except
TaskResultUnavailableError
:
missing_rows
.
append
(
row
)
result_item
=
None
except
Exception
as
exc
:
except
Exception
as
exc
:
logger
.
error
(
logger
.
error
(
f
"[pt={row.get('pt')} "
f
"[pt={row.get('pt')} "
...
@@ -1034,39 +977,173 @@ def process_rounds_to_result(
...
@@ -1034,39 +977,173 @@ def process_rounds_to_result(
raise
raise
if
result_item
is
not
None
:
if
result_item
is
not
None
:
parsed_results
.
append
((
row
,
result_item
))
parsed_results
.
append
((
row
,
result_item
))
return
parsed_results
completed_count
+=
1
if
(
completed_count
%
500
==
0
or
completed_count
==
len
(
rows
)
):
logger
.
info
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"并发解析进度: batch={batch_label}, "
f
"completed={completed_count}/{len(rows)}, "
f
"success={len(parsed_results)}, "
f
"missing={len(missing_rows)}"
)
return
parsed_results
,
missing_rows
def
insert_result_item
(
result_item
,
share_retry_count
=
0
):
def
parse_rows_with_tos_retries
(
rows
,
batch_label
):
req_id
=
result_item
.
get
(
"reqId"
)
parsed_results
,
missing_rows
=
parse_result_batch
(
if
not
bh_utils
.
insert_data
(
rows
,
"douchacha_data.geo_feishu_snipaste_result_v3"
,
batch_label
,
[
result_item
],
):
raise
ResultWriteError
(
f
"关键词统计结果写入失败: reqId={req_id}"
)
logger
.
success
(
f
"[pt={result_item.get('pt')} "
f
"count={result_item.get('`count`')} "
f
"reqId={req_id}] 关键词统计完成: "
f
"has_keyword={result_item.get('has_keyword')}, "
f
"keywords_count={result_item.get('keywords_count')}, "
f
"share_retries={share_retry_count}"
)
)
return
1
all_parsed_results
=
list
(
parsed_results
)
for
tos_retry_round
in
range
(
1
,
4
):
if
not
missing_rows
:
break
retry_values
=
[
json
.
dumps
(
{
"reqId"
:
row
.
get
(
"reqId"
),
"prompt"
:
row
.
get
(
"prompt"
),
"pt"
:
redis_pt
,
},
ensure_ascii
=
False
,
)
for
row
in
missing_rows
]
redis_client
=
init_redis
()
if
redis_client
is
None
:
raise
TaskResultUnavailableError
(
"Redis 初始化失败"
)
try
:
redis_client
.
rpush
(
redis_key
,
*
retry_values
)
except
Exception
as
exc
:
raise
TaskResultUnavailableError
(
f
"TOS 缺失任务批量重发 Redis 失败: "
f
"key={redis_key}, "
f
"retry_round={tos_retry_round}"
)
from
exc
logger
.
warning
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"TOS 结果缺失,开始第 {tos_retry_round}/3 "
f
"轮批量重试: rows={len(retry_values)}"
)
wait_mt_tasks_done
(
redis_key
,
pt
=
pt
,
count_label
=
(
f
"{start_count}-{end_count} "
f
"tos_retry={tos_retry_round}/3"
),
)
time
.
sleep
(
60
)
retry_parsed_results
,
missing_rows
=
parse_result_batch
(
missing_rows
,
(
f
"{batch_label} "
f
"tos_retry={tos_retry_round}/3"
),
)
all_parsed_results
.
extend
(
retry_parsed_results
)
if
missing_rows
:
logger
.
error
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"TOS 结果批量重试 3 次后仍缺失: "
f
"rows={len(missing_rows)}, "
f
"reqIds={[row.get('reqId') for row in missing_rows]}"
)
return
all_parsed_results
def
insert_result_items
(
result_items
,
batch_size
=
500
):
if
not
result_items
:
return
0
total_count
=
len
(
result_items
)
total_batches
=
(
total_count
+
batch_size
-
1
)
//
batch_size
inserted_count
=
0
for
batch_index
,
batch_start
in
enumerate
(
range
(
0
,
total_count
,
batch_size
),
start
=
1
,
):
batch_items
=
result_items
[
batch_start
:
batch_start
+
batch_size
]
last_error
=
None
for
attempt
in
range
(
1
,
4
):
try
:
success
=
bh_utils
.
insert_data
(
"douchacha_data."
"geo_feishu_snipaste_result_v3"
,
batch_items
,
retry_times
=
0
,
)
except
Exception
as
exc
:
last_error
=
exc
success
=
False
if
success
:
inserted_count
+=
len
(
batch_items
)
logger
.
success
(
f
"[pt={pt} "
f
"count={start_count}-{end_count}] "
f
"结果批量写入完成: "
f
"batch={batch_index}/{total_batches}, "
f
"rows={len(batch_items)}, "
f
"progress={inserted_count}/{total_count}, "
f
"attempt={attempt}/3"
)
break
logger
.
warning
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"结果批量写入失败: "
f
"batch={batch_index}/{total_batches}, "
f
"rows={len(batch_items)}, "
f
"attempt={attempt}/3, "
f
"error={last_error or 'insert_data 返回 False'}"
)
if
attempt
<
3
:
time
.
sleep
(
2
)
else
:
raise
ResultWriteError
(
f
"关键词统计结果批量写入连续失败 3 次: "
f
"pt={pt}, "
f
"count={start_count}-{end_count}, "
f
"batch={batch_index}/{total_batches}, "
f
"rows={len(batch_items)}"
)
from
last_error
return
inserted_count
inserted_count
=
0
inserted_count
=
0
parsed_results
=
parse_result_batch
(
result
,
"initial"
)
parsed_results
=
parse_rows_with_tos_retries
(
result
,
"initial"
,
)
is_count_zero
=
start_count
==
0
and
end_count
==
0
is_count_zero
=
start_count
==
0
and
end_count
==
0
if
not
is_count_zero
:
if
not
is_count_zero
:
for
_
,
result_item
in
parsed_results
:
inserted_count
=
insert_result_items
(
inserted_count
+=
insert_result_item
(
result_item
)
[
result_item
for
_
,
result_item
in
parsed_results
]
)
else
:
else
:
pending_results
=
[]
pending_results
=
[]
final_result_items
=
[]
for
row
,
result_item
in
parsed_results
:
for
row
,
result_item
in
parsed_results
:
if
int
(
result_item
.
get
(
"has_keyword"
)
or
0
)
==
1
:
if
int
(
result_item
.
get
(
"has_keyword"
)
or
0
)
==
1
:
inserted_count
+=
insert_result_item
(
result_item
)
final_result_items
.
append
(
result_item
)
else
:
else
:
pending_results
.
append
((
row
,
result_item
))
pending_results
.
append
((
row
,
result_item
))
...
@@ -1115,7 +1192,7 @@ def process_rounds_to_result(
...
@@ -1115,7 +1192,7 @@ def process_rounds_to_result(
row
row
for
row
,
_
in
pending_results
for
row
,
_
in
pending_results
]
]
retry_parsed_results
=
parse_r
esult_batch
(
retry_parsed_results
=
parse_r
ows_with_tos_retries
(
retry_rows
,
retry_rows
,
f
"count=0 retry={share_retry_round}/5"
,
f
"count=0 retry={share_retry_round}/5"
,
)
)
...
@@ -1132,19 +1209,15 @@ def process_rounds_to_result(
...
@@ -1132,19 +1209,15 @@ def process_rounds_to_result(
previous_result_item
,
previous_result_item
,
)
)
if
int
(
result_item
.
get
(
"has_keyword"
)
or
0
)
==
1
:
if
int
(
result_item
.
get
(
"has_keyword"
)
or
0
)
==
1
:
inserted_count
+=
insert_result_item
(
final_result_items
.
append
(
result_item
)
result_item
,
share_retry_count
=
share_retry_round
,
)
else
:
else
:
next_pending_results
.
append
((
row
,
result_item
))
next_pending_results
.
append
((
row
,
result_item
))
pending_results
=
next_pending_results
pending_results
=
next_pending_results
for
_
,
result_item
in
pending_results
:
for
_
,
result_item
in
pending_results
:
inserted_count
+=
insert_result_item
(
final_result_items
.
append
(
result_item
)
result_item
,
share_retry_count
=
5
,
inserted_count
=
insert_result_items
(
final_result_items
)
)
logger
.
success
(
logger
.
success
(
f
"[pt={pt} count={start_count}-{end_count}] "
f
"[pt={pt} count={start_count}-{end_count}] "
...
@@ -2060,6 +2133,6 @@ def run_daily_pipeline_safely(pt: str = None) -> None:
...
@@ -2060,6 +2133,6 @@ def run_daily_pipeline_safely(pt: str = None) -> None:
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
# run_daily_pipeline()
# run_daily_pipeline(
'20260729'
)
start_scheduler
()
start_scheduler
()
# run_daily_pipeline(pt="2026072702")
# run_daily_pipeline(pt="2026072702")
aidso_geo/core/interface.py
View file @
27ac512a
...
@@ -6,6 +6,11 @@ from aidso_geo.core.routes.feishu_interface import feishu_app
...
@@ -6,6 +6,11 @@ from aidso_geo.core.routes.feishu_interface import feishu_app
from
aidso_geo.core.routes.third_interface
import
third_app
from
aidso_geo.core.routes.third_interface
import
third_app
app
=
Flask
(
__name__
)
app
=
Flask
(
__name__
)
app
.
secret_key
=
"aidso-dashboard-session-2026-v3-fixed-key"
app
.
config
.
update
(
SESSION_COOKIE_HTTPONLY
=
True
,
SESSION_COOKIE_SAMESITE
=
"Lax"
,
)
app
.
json
.
ensure_ascii
=
False
app
.
json
.
ensure_ascii
=
False
app
.
register_blueprint
(
line_app
)
app
.
register_blueprint
(
line_app
)
...
@@ -14,4 +19,4 @@ app.register_blueprint(feishu_app)
...
@@ -14,4 +19,4 @@ app.register_blueprint(feishu_app)
app
.
register_blueprint
(
dashboard_app
)
app
.
register_blueprint
(
dashboard_app
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
app
.
run
(
host
=
'0.0.0.0'
,
port
=
8086
)
app
.
run
(
host
=
'0.0.0.0'
,
port
=
8086
)
\ No newline at end of file
aidso_geo/core/meituan_process.py
View file @
27ac512a
...
@@ -16,6 +16,7 @@ KEEP_FIELDS = (
...
@@ -16,6 +16,7 @@ KEEP_FIELDS = (
"type"
,
"type"
,
"thinkingEnabled"
,
"thinkingEnabled"
,
"channel"
,
"channel"
,
"is_share"
)
)
...
@@ -47,12 +48,12 @@ def get_result_api(task_data):
...
@@ -47,12 +48,12 @@ def get_result_api(task_data):
platform
=
task_data
.
get
(
"platform"
,
''
)
platform
=
task_data
.
get
(
"platform"
,
''
)
channel
=
task_data
.
get
(
"channel"
)
or
""
channel
=
task_data
.
get
(
"channel"
)
or
""
task_type
=
task_data
.
get
(
"type"
,
''
)
task_type
=
task_data
.
get
(
"type"
,
''
)
url
=
f
"http://172.16.1.223:8086/api/geo/task_check?reqId={req_id}"
url
=
f
"http://172.16.1.223:8086/api/geo/task_check?reqId={req_id}"
try
:
try
:
response
=
requests
.
get
(
url
)
response
=
requests
.
get
(
url
)
response_data
=
response
.
json
()
response_data
=
response
.
json
()
print
(
response
.
text
)
data
=
response_data
.
get
(
"data"
)
or
{}
data
=
response_data
.
get
(
"data"
)
or
{}
if
response_data
.
get
(
"code"
)
==
200
and
data
.
get
(
"status"
)
==
"success"
:
if
response_data
.
get
(
"code"
)
==
200
and
data
.
get
(
"status"
)
==
"success"
:
...
@@ -90,9 +91,9 @@ def task_check():
...
@@ -90,9 +91,9 @@ def task_check():
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
logger
.
info
(
"调度 启动"
)
logger
.
info
(
"调度 启动"
)
scheduler
=
BlockingScheduler
(
timezone
=
"Asia/Shanghai"
)
scheduler
=
BlockingScheduler
(
timezone
=
"Asia/Shanghai"
)
# task_check()
scheduler
.
add_job
(
scheduler
.
add_job
(
task_commit
,
task_commit
,
trigger
=
"interval"
,
trigger
=
"interval"
,
...
...
aidso_geo/core/routes/dashboard.py
View file @
27ac512a
from
datetime
import
datetime
from
datetime
import
datetime
from
hmac
import
compare_digest
from
flask
import
jsonify
,
Blueprint
,
render_template
,
request
from
flask
import
(
import
requests
jsonify
,
Blueprint
,
redirect
,
render_template
,
request
,
session
,
url_for
,
)
from
aidso_geo.config.base_config
import
init_redis
from
aidso_geo.config.base_config
import
init_redis
from
aidso_geo.utils
import
bh_utils
dashboard_app
=
Blueprint
(
"dashboard"
,
__name__
)
dashboard_app
=
Blueprint
(
"dashboard"
,
__name__
)
redis_client
=
init_redis
()
redis_client
=
init_redis
()
DASHBOARD_USERNAME
=
"aidso"
DASHBOARD_PASSWORD
=
"aidso666"
DASHBOARD_SESSION_KEY
=
"aidso_dashboard_authenticated"
@
dashboard_app
.
before_request
def
require_dashboard_login
():
if
request
.
endpoint
in
{
"dashboard.dashboard_login"
,
"dashboard.dashboard_logout"
,
}:
return
None
if
session
.
get
(
DASHBOARD_SESSION_KEY
):
return
None
if
request
.
path
.
startswith
(
"/api/queue/"
):
return
jsonify
({
"code"
:
401
,
"msg"
:
"login required"
,
"data"
:
{},
}),
401
return
redirect
(
url_for
(
"dashboard.dashboard_login"
))
@
dashboard_app
.
route
(
"/queue/login"
,
methods
=
[
"GET"
,
"POST"
])
def
dashboard_login
():
if
session
.
get
(
DASHBOARD_SESSION_KEY
):
return
redirect
(
url_for
(
"dashboard.queue_monitor_page"
))
error_message
=
""
if
request
.
method
==
"POST"
:
username
=
request
.
form
.
get
(
"username"
,
""
)
password
=
request
.
form
.
get
(
"password"
,
""
)
username_matches
=
compare_digest
(
username
,
DASHBOARD_USERNAME
,
)
password_matches
=
compare_digest
(
password
,
DASHBOARD_PASSWORD
,
)
if
username_matches
and
password_matches
:
session
.
clear
()
session
[
DASHBOARD_SESSION_KEY
]
=
True
return
redirect
(
url_for
(
"dashboard.queue_monitor_page"
))
error_message
=
"账号或密码错误,请重新输入"
return
render_template
(
"dashboard_login.html"
,
error_message
=
error_message
,
)
@
dashboard_app
.
route
(
"/queue/logout"
,
methods
=
[
"GET"
])
def
dashboard_logout
():
session
.
clear
()
return
redirect
(
url_for
(
"dashboard.dashboard_login"
))
@
dashboard_app
.
route
(
"/api/queue/status"
,
methods
=
[
"GET"
])
@
dashboard_app
.
route
(
"/api/queue/status"
,
methods
=
[
"GET"
])
def
queue_status
():
def
queue_status
():
...
@@ -48,14 +117,10 @@ def get_req_trend():
...
@@ -48,14 +117,10 @@ def get_req_trend():
}),
400
}),
400
try
:
try
:
params
=
{
begin_date
=
datetime
.
strptime
(
begin
,
"
%
Y-
%
m-
%
d"
)
"begin"
:
normalize_date_to_datetime
(
begin
,
"00:00:00"
),
end_date
=
datetime
.
strptime
(
end
,
"
%
Y-
%
m-
%
d"
)
"end"
:
normalize_date_to_datetime
(
end
,
"23:59:59"
),
}
begin_time
=
datetime
.
strptime
(
params
[
"begin"
],
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
end_time
=
datetime
.
strptime
(
params
[
"end"
],
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
if
begin_
time
>
end_tim
e
:
if
begin_
date
>
end_dat
e
:
return
jsonify
({
return
jsonify
({
"code"
:
400
,
"code"
:
400
,
"msg"
:
"begin must be earlier than end"
,
"msg"
:
"begin must be earlier than end"
,
...
@@ -68,21 +133,390 @@ def get_req_trend():
...
@@ -68,21 +133,390 @@ def get_req_trend():
"data"
:
[]
"data"
:
[]
}),
400
}),
400
url
=
"https://openapi.aidso.com/openapi/ywt/platformStats"
try
:
try
:
response
=
requests
.
get
(
url
,
params
=
params
,
timeout
=
300
)
rows
=
bh_utils
.
query_data
(
response
.
raise_for_status
()
"""
return
jsonify
(
response
.
json
())
SELECT
except
(
requests
.
RequestException
,
ValueError
)
as
e
:
platform,
pt,
uniqExactIf(reqId, channel IS NULL) AS system_count,
uniqExactIf(reqId, channel IS NOT NULL)
AS third_party_count,
uniqExact(reqId) AS task_count
FROM geo_commit_task
WHERE pt BETWEEN
%
s AND
%
s
GROUP BY platform, pt
ORDER BY pt, platform
"""
,
(
begin_date
.
strftime
(
"
%
Y
%
m
%
d"
),
end_date
.
strftime
(
"
%
Y
%
m
%
d"
),
),
)
if
rows
is
None
:
raise
RuntimeError
(
"query geo_commit_task failed"
)
data
=
[
{
"platform"
:
row
.
get
(
"platform"
),
"time"
:
datetime
.
strptime
(
str
(
row
.
get
(
"pt"
)),
"
%
Y
%
m
%
d"
,
)
.
strftime
(
"
%
Y-
%
m-
%
d"
),
"system_count"
:
int
(
row
.
get
(
"system_count"
)
or
0
),
"third_party_count"
:
int
(
row
.
get
(
"third_party_count"
)
or
0
),
"count"
:
int
(
row
.
get
(
"task_count"
)
or
0
),
}
for
row
in
rows
]
return
jsonify
({
"code"
:
200
,
"msg"
:
"success"
,
"data"
:
data
,
})
except
(
TypeError
,
ValueError
,
RuntimeError
)
as
e
:
return
jsonify
({
return
jsonify
({
"code"
:
500
,
"code"
:
500
,
"msg"
:
f
"platformStats
request
failed: {e}"
,
"msg"
:
f
"platformStats
query
failed: {e}"
,
"data"
:
[]
"data"
:
[]
}),
500
}),
500
def
normalize_date_to_datetime
(
value
,
default_time
):
@
dashboard_app
.
route
(
"/api/queue/thirdPartyStats"
,
methods
=
[
"GET"
])
date_value
=
value
.
replace
(
"T"
,
" "
)
.
strip
()[:
10
]
def
get_third_party_stats
():
datetime
.
strptime
(
date_value
,
"
%
Y-
%
m-
%
d"
)
begin
=
request
.
args
.
get
(
"begin"
,
""
)
.
strip
()
return
f
"{date_value} {default_time}"
end
=
request
.
args
.
get
(
"end"
,
""
)
.
strip
()
selected_channel
=
request
.
args
.
get
(
"channel"
,
""
)
.
strip
()
selected_platform
=
request
.
args
.
get
(
"platform"
,
""
)
.
strip
()
if
not
begin
or
not
end
:
return
jsonify
({
"code"
:
400
,
"msg"
:
"begin and end are required"
,
"data"
:
{},
}),
400
try
:
begin_date
=
datetime
.
strptime
(
begin
,
"
%
Y-
%
m-
%
d"
)
end_date
=
datetime
.
strptime
(
end
,
"
%
Y-
%
m-
%
d"
)
if
begin_date
>
end_date
:
return
jsonify
({
"code"
:
400
,
"msg"
:
"begin must be earlier than end"
,
"data"
:
{},
}),
400
except
ValueError
:
return
jsonify
({
"code"
:
400
,
"msg"
:
"begin and end must be YYYY-MM-DD"
,
"data"
:
{},
}),
400
begin_pt
=
begin_date
.
strftime
(
"
%
Y
%
m
%
d"
)
end_pt
=
end_date
.
strftime
(
"
%
Y
%
m
%
d"
)
day_count
=
(
end_date
-
begin_date
)
.
days
+
1
try
:
enabled_channels
=
bh_utils
.
query_data
(
"""
SELECT
channel,
remark,
daily_limit,
total_limit,
allow_platforms
FROM geo_third_token
WHERE status = 1
ORDER BY channel
"""
)
if
enabled_channels
is
None
:
raise
RuntimeError
(
"query enabled third channels failed"
)
enabled_channel_names
=
[
str
(
row
.
get
(
"channel"
)
or
""
)
.
strip
()
for
row
in
enabled_channels
if
str
(
row
.
get
(
"channel"
)
or
""
)
.
strip
()
]
enabled_channel_set
=
set
(
enabled_channel_names
)
if
(
selected_channel
and
selected_channel
not
in
enabled_channel_set
):
return
jsonify
({
"code"
:
400
,
"msg"
:
"selected channel is not enabled"
,
"data"
:
{},
}),
400
if
not
enabled_channel_names
:
return
jsonify
({
"code"
:
200
,
"msg"
:
"success"
,
"data"
:
{
"metrics"
:
{
"third_total"
:
0
,
"daily_average"
:
0
,
"third_ratio"
:
0
,
"share_total"
:
0
,
"enabled_channel_count"
:
0
,
},
"channels"
:
[],
"platforms"
:
[],
"daily"
:
[],
"channel_ranking"
:
[],
"matrix"
:
[],
"channel_details"
:
[],
},
})
placeholders
=
", "
.
join
(
[
"
%
s"
]
*
len
(
enabled_channel_names
)
)
usage_filters
=
[
"pt BETWEEN
%
s AND
%
s"
,
f
"channel IN ({placeholders})"
,
]
usage_params
=
[
begin_pt
,
end_pt
,
*
enabled_channel_names
,
]
if
selected_channel
:
usage_filters
.
append
(
"channel =
%
s"
)
usage_params
.
append
(
selected_channel
)
if
selected_platform
:
usage_filters
.
append
(
"platform =
%
s"
)
usage_params
.
append
(
selected_platform
)
usage_where
=
" AND "
.
join
(
usage_filters
)
usage_rows
=
bh_utils
.
query_data
(
f
"""
SELECT
channel,
platform,
pt,
uniqExact(reqId) AS task_count,
uniqExactIf(
reqId,
ifNull(toString(is_share), '0') = '1'
) AS share_count
FROM geo_commit_task
WHERE {usage_where}
GROUP BY channel, platform, pt
ORDER BY pt, channel, platform
"""
,
tuple
(
usage_params
),
)
if
usage_rows
is
None
:
raise
RuntimeError
(
"query third party usage failed"
)
summary_rows
=
bh_utils
.
query_data
(
f
"""
SELECT
uniqExact(reqId) AS third_total,
uniqExactIf(
reqId,
ifNull(toString(is_share), '0') = '1'
) AS share_total
FROM geo_commit_task
WHERE {usage_where}
"""
,
tuple
(
usage_params
),
)
if
summary_rows
is
None
:
raise
RuntimeError
(
"query third party summary failed"
)
total_filters
=
[
"pt BETWEEN
%
s AND
%
s"
]
total_params
=
[
begin_pt
,
end_pt
]
if
selected_platform
:
total_filters
.
append
(
"platform =
%
s"
)
total_params
.
append
(
selected_platform
)
total_rows
=
bh_utils
.
query_data
(
f
"""
SELECT uniqExact(reqId) AS all_total
FROM geo_commit_task
WHERE {" AND ".join(total_filters)}
"""
,
tuple
(
total_params
),
)
if
total_rows
is
None
:
raise
RuntimeError
(
"query all task summary failed"
)
third_total
=
int
(
(
summary_rows
[
0
]
if
summary_rows
else
{})
.
get
(
"third_total"
)
or
0
)
share_total
=
int
(
(
summary_rows
[
0
]
if
summary_rows
else
{})
.
get
(
"share_total"
)
or
0
)
all_total
=
int
(
(
total_rows
[
0
]
if
total_rows
else
{})
.
get
(
"all_total"
)
or
0
)
daily_map
=
{}
channel_map
=
{}
matrix_map
=
{}
platform_set
=
set
()
for
row
in
usage_rows
:
channel
=
str
(
row
.
get
(
"channel"
)
or
""
)
platform
=
str
(
row
.
get
(
"platform"
)
or
""
)
pt
=
str
(
row
.
get
(
"pt"
)
or
""
)
task_count
=
int
(
row
.
get
(
"task_count"
)
or
0
)
share_count
=
int
(
row
.
get
(
"share_count"
)
or
0
)
platform_set
.
add
(
platform
)
daily_key
=
(
channel
,
pt
)
daily_map
[
daily_key
]
=
(
daily_map
.
get
(
daily_key
,
0
)
+
task_count
)
channel_item
=
channel_map
.
setdefault
(
channel
,
{
"channel"
:
channel
,
"task_count"
:
0
,
"share_count"
:
0
,
"platforms"
:
set
(),
"last_date"
:
""
,
},
)
channel_item
[
"task_count"
]
+=
task_count
channel_item
[
"share_count"
]
+=
share_count
channel_item
[
"platforms"
]
.
add
(
platform
)
if
not
channel_item
[
"last_date"
]
or
(
pt
>
channel_item
[
"last_date"
]
):
channel_item
[
"last_date"
]
=
pt
matrix_key
=
(
channel
,
platform
)
matrix_map
[
matrix_key
]
=
(
matrix_map
.
get
(
matrix_key
,
0
)
+
task_count
)
config_map
=
{
str
(
row
.
get
(
"channel"
)
or
""
):
row
for
row
in
enabled_channels
}
ranking
=
[]
details
=
[]
channels_to_show
=
(
[
selected_channel
]
if
selected_channel
else
enabled_channel_names
)
for
channel
in
channels_to_show
:
config
=
config_map
.
get
(
channel
)
or
{}
usage
=
channel_map
.
get
(
channel
)
or
{
"task_count"
:
0
,
"share_count"
:
0
,
"platforms"
:
set
(),
"last_date"
:
""
,
}
task_count
=
int
(
usage
[
"task_count"
])
item
=
{
"channel"
:
channel
,
"remark"
:
config
.
get
(
"remark"
)
or
""
,
"task_count"
:
task_count
,
"share_count"
:
int
(
usage
[
"share_count"
]),
"ratio"
:
round
(
task_count
/
third_total
*
100
,
2
,
)
if
third_total
else
0
,
"platform_count"
:
len
(
usage
[
"platforms"
]),
"last_date"
:
usage
[
"last_date"
],
"daily_limit"
:
int
(
config
.
get
(
"daily_limit"
)
or
0
),
"total_limit"
:
int
(
config
.
get
(
"total_limit"
)
or
0
),
"allow_platforms"
:
(
config
.
get
(
"allow_platforms"
)
or
"[]"
),
}
ranking
.
append
(
item
)
details
.
append
(
dict
(
item
))
ranking
.
sort
(
key
=
lambda
item
:
item
[
"task_count"
],
reverse
=
True
,
)
details
.
sort
(
key
=
lambda
item
:
item
[
"task_count"
],
reverse
=
True
,
)
platforms
=
sorted
(
platform_set
)
daily
=
[
{
"channel"
:
channel
,
"pt"
:
pt
,
"count"
:
int
(
task_count
),
}
for
(
channel
,
pt
),
task_count
in
sorted
(
daily_map
.
items
()
)
]
matrix
=
[
{
"channel"
:
channel
,
"platform"
:
platform
,
"count"
:
int
(
matrix_map
.
get
((
channel
,
platform
),
0
)
),
}
for
channel
in
channels_to_show
for
platform
in
platforms
]
return
jsonify
({
"code"
:
200
,
"msg"
:
"success"
,
"data"
:
{
"metrics"
:
{
"third_total"
:
third_total
,
"daily_average"
:
round
(
third_total
/
day_count
,
2
,
),
"third_ratio"
:
round
(
third_total
/
all_total
*
100
,
2
,
)
if
all_total
else
0
,
"share_total"
:
share_total
,
"enabled_channel_count"
:
len
(
enabled_channel_names
),
},
"channels"
:
[
{
"channel"
:
row
.
get
(
"channel"
),
"remark"
:
row
.
get
(
"remark"
)
or
""
,
}
for
row
in
enabled_channels
],
"platforms"
:
platforms
,
"daily"
:
daily
,
"channel_ranking"
:
ranking
,
"matrix"
:
matrix
,
"channel_details"
:
details
,
},
})
except
(
TypeError
,
ValueError
,
RuntimeError
)
as
exc
:
return
jsonify
({
"code"
:
500
,
"msg"
:
f
"thirdPartyStats query failed: {exc}"
,
"data"
:
{},
}),
500
aidso_geo/core/routes/third_interface.py
View file @
27ac512a
...
@@ -502,12 +502,57 @@ def mt_task_commit():
...
@@ -502,12 +502,57 @@ def mt_task_commit():
if
type
not
in
{
"0"
,
"1"
}:
if
type
not
in
{
"0"
,
"1"
}:
return
err
(
400
,
f
"platform[{idx}].type must be '0' or '1'"
)
return
err
(
400
,
f
"platform[{idx}].type must be '0' or '1'"
)
is_share
=
str
(
item
.
get
(
"is_share"
,
"0"
))
if
is_share
not
in
{
"0"
,
"1"
}:
return
err
(
400
,
f
"platform[{idx}].is_share must be '0' or '1'"
)
normalized
.
append
({
normalized
.
append
({
"name"
:
name
,
"name"
:
name
,
"thinkingEnabled"
:
thinking_enabled
,
"thinkingEnabled"
:
thinking_enabled
,
"type"
:
type
"type"
:
type
,
"is_share"
:
is_share
})
})
share_requested_platforms
=
{
item
[
"name"
]
for
item
in
normalized
if
item
[
"is_share"
]
==
"1"
}
if
share_requested_platforms
:
try
:
share_platforms_raw
=
tos_utils
.
get_string_from_tos
(
"geo_config/share_platforms.json"
)
share_platforms
=
json
.
loads
(
share_platforms_raw
)
except
Exception
:
traceback
.
print_exc
()
return
err
(
500
,
"分享平台配置读取失败"
,
500
)
if
not
isinstance
(
share_platforms
,
list
):
return
err
(
500
,
"分享平台配置格式错误,必须是平台数组"
,
500
)
supported_share_platforms
=
{
str
(
platform
)
.
strip
()
for
platform
in
share_platforms
if
str
(
platform
)
.
strip
()
}
unsupported_share_platforms
=
sorted
(
share_requested_platforms
-
supported_share_platforms
)
if
unsupported_share_platforms
:
return
err
(
400
,
"平台不支持分享: "
+
", "
.
join
(
unsupported_share_platforms
)
)
platform_costs
=
parse_platform_costs
(
auth_info
.
get
(
"platform_costs"
))
platform_costs
=
parse_platform_costs
(
auth_info
.
get
(
"platform_costs"
))
thinking_costs
=
parse_thinking_costs
(
auth_info
.
get
(
"thinking_costs"
))
thinking_costs
=
parse_thinking_costs
(
auth_info
.
get
(
"thinking_costs"
))
...
@@ -563,6 +608,7 @@ def mt_task_commit():
...
@@ -563,6 +608,7 @@ def mt_task_commit():
"platform"
:
name
,
"platform"
:
name
,
"thinkingEnabled"
:
p
[
"thinkingEnabled"
],
"thinkingEnabled"
:
p
[
"thinkingEnabled"
],
"type"
:
type_map
[
p
[
"type"
]],
"type"
:
type_map
[
p
[
"type"
]],
"is_share"
:
p
[
"is_share"
],
"insertime"
:
now_ts
,
"insertime"
:
now_ts
,
"status"
:
"ING"
,
"status"
:
"ING"
,
"channel"
:
channel
"channel"
:
channel
...
@@ -660,7 +706,8 @@ def mt_get_result():
...
@@ -660,7 +706,8 @@ def mt_get_result():
"think"
:
"think.txt"
,
"think"
:
"think.txt"
,
"context"
:
"context.txt"
,
"context"
:
"context.txt"
,
"suggestions"
:
"suggestions.txt"
,
"suggestions"
:
"suggestions.txt"
,
"rich_media_block"
:
"rich_media_block.txt"
"rich_media_block"
:
"rich_media_block.txt"
,
"share_url"
:
"share_url.text"
}
}
result
=
[]
result
=
[]
...
@@ -797,4 +844,3 @@ def mt_get_usage():
...
@@ -797,4 +844,3 @@ def mt_get_usage():
# "history_used_list": history_userd_list
# "history_used_list": history_userd_list
})
})
aidso_geo/core/static/dashboard/aidso_logo.png
0 → 100644
View file @
27ac512a
17.9 KB
aidso_geo/core/static/dashboard/dashboard_login.css
0 → 100644
View file @
27ac512a
*
{
box-sizing
:
border-box
;
}
:root
{
--primary
:
#7138f4
;
--primary-dark
:
#5824d6
;
--ink
:
#282735
;
--muted
:
#7b788a
;
--line
:
#e2deea
;
}
body
{
margin
:
0
;
color
:
var
(
--ink
);
font-family
:
-apple-system
,
BlinkMacSystemFont
,
"Segoe UI"
,
"PingFang SC"
,
"Microsoft YaHei"
,
Arial
,
sans-serif
;
background
:
#eef3ff
;
}
.login-page
{
display
:
grid
;
min-height
:
100vh
;
grid-template-columns
:
minmax
(
420px
,
1.08
fr
)
minmax
(
430px
,
0.92
fr
);
}
.login-visual
{
position
:
relative
;
display
:
flex
;
align-items
:
center
;
overflow
:
hidden
;
padding
:
clamp
(
50px
,
7vw
,
110px
);
background
:
radial-gradient
(
circle
at
22%
18%
,
rgba
(
255
,
255
,
255
,
0.22
),
transparent
26%
),
radial-gradient
(
circle
at
84%
84%
,
rgba
(
193
,
164
,
255
,
0.3
),
transparent
30%
),
linear-gradient
(
145deg
,
#6322eb
0%
,
#7f3fff
54%
,
#985fff
100%
);
}
.login-visual
::before
,
.login-visual
::after
{
content
:
""
;
position
:
absolute
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.18
);
border-radius
:
50%
;
}
.login-visual
::before
{
width
:
520px
;
height
:
520px
;
top
:
-270px
;
right
:
-180px
;
}
.login-visual
::after
{
width
:
360px
;
height
:
360px
;
bottom
:
-205px
;
left
:
-130px
;
}
.visual-content
{
position
:
relative
;
z-index
:
1
;
max-width
:
580px
;
color
:
#fff
;
}
.visual-badge
{
display
:
inline-flex
;
padding
:
7px
11px
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.32
);
border-radius
:
999px
;
background
:
rgba
(
255
,
255
,
255
,
0.1
);
font-size
:
11px
;
font-weight
:
800
;
letter-spacing
:
0.15em
;
}
.visual-content
h1
{
margin
:
24px
0
15px
;
font-size
:
clamp
(
42px
,
5.2vw
,
74px
);
font-weight
:
850
;
letter-spacing
:
-0.055em
;
line-height
:
1.05
;
}
.visual-content
p
{
max-width
:
500px
;
margin
:
0
;
color
:
rgba
(
255
,
255
,
255
,
0.78
);
font-size
:
clamp
(
15px
,
1.25vw
,
19px
);
line-height
:
1.8
;
}
.visual-grid
{
display
:
grid
;
width
:
min
(
100%
,
470px
);
grid-template-columns
:
repeat
(
3
,
1
fr
);
gap
:
12px
;
margin-top
:
52px
;
}
.visual-grid
span
{
height
:
74px
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.17
);
border-radius
:
14px
;
background
:
rgba
(
255
,
255
,
255
,
0.08
);
box-shadow
:
inset
0
1px
0
rgba
(
255
,
255
,
255
,
0.1
);
}
.visual-grid
span
:nth-child
(
2
),
.visual-grid
span
:nth-child
(
4
),
.visual-grid
span
:nth-child
(
6
)
{
background
:
rgba
(
255
,
255
,
255
,
0.15
);
}
.login-panel
{
display
:
grid
;
min-width
:
0
;
place-items
:
center
;
padding
:
42px
;
background
:
radial-gradient
(
circle
at
100%
0%
,
rgba
(
113
,
56
,
244
,
0.08
),
transparent
28%
),
#f7f8fc
;
}
.login-card
{
width
:
min
(
100%
,
440px
);
padding
:
42px
;
border
:
1px
solid
rgba
(
226
,
222
,
234
,
0.9
);
border-radius
:
22px
;
background
:
rgba
(
255
,
255
,
255
,
0.96
);
box-shadow
:
0
22px
55px
rgba
(
58
,
45
,
88
,
0.1
);
}
.login-logo
{
display
:
block
;
width
:
240px
;
max-width
:
78%
;
height
:
auto
;
margin-bottom
:
42px
;
}
.login-heading
h2
{
margin
:
0
;
color
:
#242230
;
font-size
:
28px
;
font-weight
:
850
;
letter-spacing
:
-0.025em
;
}
.login-heading
p
{
margin
:
9px
0
30px
;
color
:
var
(
--muted
);
font-size
:
14px
;
}
.login-field
{
display
:
grid
;
gap
:
8px
;
margin-bottom
:
20px
;
color
:
#565267
;
font-size
:
13px
;
font-weight
:
750
;
}
.input-shell
{
position
:
relative
;
display
:
flex
;
align-items
:
center
;
}
.input-shell
input
{
width
:
100%
;
height
:
50px
;
padding
:
0
14px
0
44px
;
border
:
1px
solid
var
(
--line
);
border-radius
:
11px
;
outline
:
none
;
background
:
#fff
;
color
:
#292635
;
font
:
inherit
;
font-size
:
14px
;
transition
:
border-color
0.18s
ease
,
box-shadow
0.18s
ease
;
}
.input-shell
input
::placeholder
{
color
:
#aaa7b2
;
}
.input-shell
input
:focus
{
border-color
:
var
(
--primary
);
box-shadow
:
0
0
0
4px
rgba
(
113
,
56
,
244
,
0.11
);
}
.input-icon
{
position
:
absolute
;
z-index
:
1
;
left
:
17px
;
width
:
14px
;
height
:
14px
;
pointer-events
:
none
;
}
.user-icon
{
border
:
2px
solid
#9994a5
;
border-radius
:
50%
;
}
.user-icon
::after
{
content
:
""
;
position
:
absolute
;
width
:
18px
;
height
:
9px
;
top
:
12px
;
left
:
-4px
;
border
:
2px
solid
#9994a5
;
border-bottom
:
0
;
border-radius
:
12px
12px
0
0
;
}
.lock-icon
{
top
:
18px
;
border
:
2px
solid
#9994a5
;
border-radius
:
3px
;
}
.lock-icon
::before
{
content
:
""
;
position
:
absolute
;
width
:
8px
;
height
:
8px
;
top
:
-9px
;
left
:
1px
;
border
:
2px
solid
#9994a5
;
border-bottom
:
0
;
border-radius
:
8px
8px
0
0
;
}
.login-error
{
margin
:
-3px
0
17px
;
padding
:
10px
12px
;
border
:
1px
solid
#fecaca
;
border-radius
:
9px
;
background
:
#fff4f4
;
color
:
#b42323
;
font-size
:
13px
;
}
.login-button
{
width
:
100%
;
height
:
50px
;
margin-top
:
5px
;
border
:
0
;
border-radius
:
11px
;
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
color
:
#fff
;
cursor
:
pointer
;
font-size
:
15px
;
font-weight
:
800
;
box-shadow
:
0
11px
22px
rgba
(
113
,
56
,
244
,
0.24
);
transition
:
transform
0.18s
ease
,
box-shadow
0.18s
ease
;
}
.login-button
:hover
{
transform
:
translateY
(
-1px
);
box-shadow
:
0
14px
28px
rgba
(
113
,
56
,
244
,
0.3
);
}
.login-footer
{
margin
:
27px
0
0
;
color
:
#aaa7b2
;
font-size
:
11px
;
text-align
:
center
;
}
@media
(
max-width
:
900px
)
{
.login-page
{
grid-template-columns
:
1
fr
;
}
.login-visual
{
min-height
:
260px
;
padding
:
50px
34px
;
}
.visual-content
h1
{
margin-top
:
16px
;
font-size
:
42px
;
}
.visual-grid
{
display
:
none
;
}
.login-panel
{
padding
:
34px
20px
;
}
}
@media
(
max-width
:
520px
)
{
.login-visual
{
min-height
:
220px
;
padding
:
38px
24px
;
}
.visual-content
h1
{
font-size
:
34px
;
}
.visual-content
p
{
font-size
:
13px
;
}
.login-card
{
padding
:
30px
24px
;
border-radius
:
18px
;
}
.login-logo
{
width
:
210px
;
margin-bottom
:
34px
;
}
}
aidso_geo/core/static/dashboard/queue_monitor.css
View file @
27ac512a
...
@@ -196,6 +196,43 @@
...
@@ -196,6 +196,43 @@
display
:
flex
;
display
:
flex
;
}
}
.third-panel
{
position
:
relative
;
display
:
grid
;
gap
:
18px
;
min-width
:
0
;
overflow
:
hidden
;
padding
:
18px
;
border
:
1px
solid
#e5e7eb
;
border-radius
:
14px
;
background
:
#fff
;
box-shadow
:
0
14px
30px
rgba
(
15
,
23
,
42
,
0.06
);
}
.third-panel.loading
.trend-loading-mask
{
display
:
flex
;
}
.third-panel
>
.filter-row
{
display
:
grid
;
grid-template-columns
:
auto
minmax
(
170px
,
1
fr
)
minmax
(
170px
,
1
fr
)
minmax
(
170px
,
1
fr
)
minmax
(
170px
,
1
fr
);
align-items
:
end
;
gap
:
12px
;
margin-bottom
:
0
;
}
.third-panel
>
.filter-row
.field
,
.third-panel
>
.filter-row
input
,
.third-panel
>
.filter-row
select
{
min-width
:
0
;
width
:
100%
;
}
.loading-box
{
.loading-box
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
...
@@ -240,6 +277,7 @@
...
@@ -240,6 +277,7 @@
.quick-range
{
.quick-range
{
display
:
flex
;
display
:
flex
;
gap
:
8px
;
gap
:
8px
;
align-items
:
flex-end
;
}
}
.field
{
.field
{
...
@@ -253,212 +291,695 @@
...
@@ -253,212 +291,695 @@
color
:
#6b7280
;
color
:
#6b7280
;
}
}
.field
input
{
.field
input
,
.field
select
{
min-width
:
220px
;
min-width
:
220px
;
border
:
1px
solid
#d7dce3
;
height
:
42px
;
border-radius
:
8px
;
box-sizing
:
border-box
;
padding
:
9px
10px
;
border
:
1px
solid
#cbd5e1
;
border-radius
:
9px
;
padding
:
0
12px
;
background
:
#fff
;
background
:
#fff
;
color
:
#1f2937
;
color
:
#334155
;
font-size
:
14px
;
font-family
:
inherit
;
font-weight
:
600
;
line-height
:
40px
;
transition
:
border-color
0.18s
ease
,
box-shadow
0.18s
ease
;
}
.field
select
{
appearance
:
none
;
-webkit-appearance
:
none
;
padding-right
:
38px
;
background-image
:
linear-gradient
(
45deg
,
transparent
50%
,
#64748b
50%
),
linear-gradient
(
135deg
,
#64748b
50%
,
transparent
50%
);
background-position
:
calc
(
100%
-
17px
)
17px
,
calc
(
100%
-
12px
)
17px
;
background-size
:
5px
5px
,
5px
5px
;
background-repeat
:
no-repeat
;
cursor
:
pointer
;
}
.field
select
.native-select-hidden
{
display
:
none
;
}
.custom-select
{
position
:
relative
;
width
:
100%
;
min-width
:
0
;
}
.custom-select-trigger
{
display
:
flex
;
width
:
100%
;
height
:
42px
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
12px
;
box-sizing
:
border-box
;
padding
:
0
13px
;
border
:
1px
solid
#cbd5e1
;
border-radius
:
9px
;
background
:
#fff
;
color
:
#334155
;
font-family
:
inherit
;
font-size
:
14px
;
font-size
:
14px
;
font-weight
:
600
;
text-align
:
left
;
cursor
:
pointer
;
transition
:
border-color
0.18s
ease
,
box-shadow
0.18s
ease
;
}
}
.field
input
:focus
{
.custom-select-trigger
:hover
{
border-color
:
#94a3b8
;
}
.custom-select.open
.custom-select-trigger
,
.custom-select-trigger
:focus-visible
{
outline
:
none
;
outline
:
none
;
border-color
:
#2563eb
;
border-color
:
#2563eb
;
box-shadow
:
0
0
0
3px
rgba
(
37
,
99
,
235
,
0.12
);
box-shadow
:
0
0
0
3px
rgba
(
37
,
99
,
235
,
0.12
);
}
}
.trend-chart
{
.custom-select-value
{
width
:
100%
;
min-width
:
0
;
min-height
:
300px
;
overflow
:
hidden
;
overflow-x
:
auto
;
text-overflow
:
ellipsis
;
border-radius
:
12px
;
white-space
:
nowrap
;
background
:
#f8fafc
;
border
:
1px
solid
#edf2f7
;
}
}
.daily-grid
{
.custom-select-arrow
{
display
:
grid
;
width
:
8px
;
grid-auto-flow
:
column
;
height
:
8px
;
grid-auto-columns
:
minmax
(
320px
,
1
fr
);
flex
:
0
0
auto
;
gap
:
14px
;
margin
:
-4px
2px
0
0
;
min-width
:
100%
;
border-right
:
2px
solid
#64748b
;
padding
:
14px
;
border-bottom
:
2px
solid
#64748b
;
transform
:
rotate
(
45deg
);
transition
:
transform
0.18s
ease
,
margin
0.18s
ease
;
}
}
.day-card
{
.custom-select.open
.custom-select-arrow
{
min-width
:
0
;
margin-top
:
4px
;
border
:
1px
solid
#e5e7eb
;
transform
:
rotate
(
225deg
);
}
.custom-select-menu
{
position
:
absolute
;
z-index
:
120
;
top
:
calc
(
100%
+
8px
);
left
:
0
;
display
:
none
;
width
:
max
(
100%
,
250px
);
max-width
:
min
(
420px
,
80vw
);
max-height
:
320px
;
overflow-y
:
auto
;
box-sizing
:
border-box
;
padding
:
6px
;
border
:
1px
solid
#dbe3ee
;
border-radius
:
12px
;
border-radius
:
12px
;
background
:
#fff
;
background
:
#fff
;
box-shadow
:
0
1
0px
22px
rgba
(
15
,
23
,
42
,
0.04
);
box-shadow
:
0
1
8px
45px
rgba
(
15
,
23
,
42
,
0.16
);
}
}
.day-card-head
{
.custom-select.open
.custom-select-menu
{
display
:
flex
;
display
:
grid
;
align-items
:
center
;
gap
:
3px
;
justify-content
:
space-between
;
padding
:
14px
14px
10px
;
border-bottom
:
1px
solid
#edf2f7
;
}
}
.day-date
{
.custom-select-option
{
color
:
#111827
;
width
:
100%
;
font-size
:
16px
;
min-height
:
38px
;
font-weight
:
800
;
padding
:
8px
11px
;
border
:
0
;
border-radius
:
8px
;
background
:
transparent
;
color
:
#334155
;
font-family
:
inherit
;
font-size
:
14px
;
font-weight
:
600
;
line-height
:
1.35
;
text-align
:
left
;
cursor
:
pointer
;
}
}
.day-total
{
.custom-select-option
:hover
{
color
:
#64748b
;
background
:
#eff6ff
;
font-size
:
13px
;
color
:
#1d4ed8
;
font-weight
:
700
;
}
}
.day-list
{
.custom-select-option.selected
{
display
:
grid
;
position
:
relative
;
gap
:
10px
;
padding-right
:
34px
;
padding
:
12px
14px
14px
;
background
:
#2563eb
;
color
:
#fff
;
}
.custom-select-option.selected
::after
{
content
:
"✓"
;
position
:
absolute
;
top
:
50%
;
right
:
12px
;
font-size
:
15px
;
transform
:
translateY
(
-50%
);
}
.field
input
[
type
=
"date"
]
{
color-scheme
:
light
;
cursor
:
pointer
;
}
.field
input
:hover
,
.field
select
:hover
{
border-color
:
#94a3b8
;
}
.field
input
:focus
,
.field
select
:focus
{
outline
:
none
;
border-color
:
#2563eb
;
box-shadow
:
0
0
0
3px
rgba
(
37
,
99
,
235
,
0.12
);
}
}
.day-row
{
.filter-row
.quick-btn
{
height
:
42px
;
box-sizing
:
border-box
;
}
.third-metrics
{
display
:
grid
;
display
:
grid
;
grid-template-columns
:
minmax
(
100px
,
1
fr
)
minmax
(
90px
,
42%
)
58px
;
grid-template-columns
:
repeat
(
5
,
minmax
(
0
,
1
fr
));
align-items
:
center
;
gap
:
14px
;
gap
:
8px
;
min-height
:
24px
;
}
}
.day-platform
{
.third-metric-card
,
color
:
#334155
;
.third-section-card
{
border
:
1px
solid
#e5e7eb
;
border-radius
:
13px
;
background
:
#fff
;
box-shadow
:
0
8px
22px
rgba
(
15
,
23
,
42
,
0.05
);
}
.third-metric-card
{
display
:
flex
;
flex-direction
:
column
;
gap
:
9px
;
min-width
:
0
;
padding
:
16px
;
}
.third-metric-card
span
{
color
:
#64748b
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
700
;
font-weight
:
700
;
}
.third-metric-card
strong
{
overflow
:
hidden
;
overflow
:
hidden
;
color
:
#0f172a
;
font-size
:
clamp
(
19px
,
1.45vw
,
25px
);
font-weight
:
800
;
line-height
:
1.2
;
text-overflow
:
ellipsis
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
white-space
:
nowrap
;
}
}
.day-bar
{
.third-dashboard-grid
{
height
:
9px
;
display
:
grid
;
border-radius
:
999px
;
grid-template-columns
:
minmax
(
360px
,
0.8
fr
)
minmax
(
0
,
1.7
fr
);
background
:
#eef2f7
;
gap
:
18px
;
min-width
:
0
;
}
.third-section-card
{
min-width
:
0
;
overflow
:
hidden
;
overflow
:
hidden
;
}
}
.day-bar-inner
{
.third-card-head
{
height
:
100%
;
display
:
flex
;
min-width
:
3px
;
align-items
:
center
;
border-radius
:
999px
;
justify-content
:
space-between
;
gap
:
12px
;
padding
:
15px
17px
;
border-bottom
:
1px
solid
#edf2f7
;
}
}
.day-count
{
.third-card-head
strong
{
color
:
#111827
;
color
:
#0f172a
;
font-size
:
13px
;
font-size
:
16px
;
font-weight
:
800
;
}
.third-card-head
>
span
{
color
:
#64748b
;
font-size
:
12px
;
font-weight
:
700
;
font-weight
:
700
;
text-align
:
right
;
}
}
.chart-empty
{
.third-ranking-list
{
display
:
flex
;
display
:
grid
;
gap
:
9px
;
max-height
:
390px
;
overflow-y
:
auto
;
padding
:
14px
16px
17px
;
}
.third-ranking-row
{
display
:
grid
;
grid-template-columns
:
26px
minmax
(
110px
,
1.2
fr
)
minmax
(
80px
,
1
fr
)
64px
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
gap
:
9px
;
min-height
:
220px
;
width
:
100%
;
color
:
#6b7280
;
padding
:
7px
4px
;
font-size
:
14px
;
border
:
none
;
border-radius
:
8px
;
background
:
transparent
;
cursor
:
pointer
;
text-align
:
left
;
}
}
.trend-summary
{
.third-ranking-row
:hover
{
margin-top
:
16px
;
background
:
#f8fafc
;
padding
:
16px
;
border
:
1px
solid
#edf2f7
;
border-radius
:
12px
;
background
:
linear-gradient
(
180deg
,
#ffffff
0%
,
#f8fafc
100%
);
}
}
.t
rend-summary-title
{
.t
hird-ranking-index
{
display
:
flex
;
display
:
inline-
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
space-between
;
justify-content
:
center
;
margin-bottom
:
12px
;
width
:
22px
;
height
:
22px
;
border-radius
:
7px
;
background
:
#eff6ff
;
color
:
#2563eb
;
font-size
:
11px
;
font-weight
:
800
;
}
}
.trend-summary-title-main
{
.third-ranking-name
{
color
:
#111827
;
overflow
:
hidden
;
font-size
:
16px
;
color
:
#334155
;
font-size
:
12px
;
font-weight
:
800
;
font-weight
:
800
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
}
.trend-summary-actions
{
.third-ranking-bar
{
display
:
flex
;
height
:
8px
;
align-items
:
center
;
overflow
:
hidden
;
gap
:
10px
;
border-radius
:
999px
;
background
:
#edf2f7
;
}
}
.trend-summary-range
{
.third-ranking-bar-inner
{
color
:
#64748b
;
display
:
block
;
font-size
:
13px
;
height
:
100%
;
font-weight
:
700
;
border-radius
:
inherit
;
background
:
#2563eb
;
}
}
.trend-switch
{
.third-ranking-count
{
color
:
#0f172a
;
font-size
:
12px
;
text-align
:
right
;
}
.third-trend-switch
{
display
:
inline-grid
;
display
:
inline-grid
;
grid-template-columns
:
1
fr
1
fr
;
grid-template-columns
:
1
fr
1
fr
;
gap
:
2px
;
gap
:
2px
;
padding
:
3px
;
padding
:
3px
;
border
:
1px
solid
#d7dce3
;
border
:
1px
solid
#d7dce3
;
border-radius
:
10
px
;
border-radius
:
9
px
;
background
:
#f1f5f9
;
background
:
#f1f5f9
;
}
}
.t
rend-switch-bt
n
{
.t
hird-trend-switch
butto
n
{
border
:
none
;
border
:
none
;
border-radius
:
7
px
;
border-radius
:
6
px
;
background
:
transparent
;
background
:
transparent
;
color
:
#64748b
;
color
:
#64748b
;
padding
:
6px
11
px
;
padding
:
6px
9
px
;
cursor
:
pointer
;
cursor
:
pointer
;
font-size
:
1
3
px
;
font-size
:
1
2
px
;
font-weight
:
800
;
font-weight
:
800
;
}
}
.t
rend-switch-bt
n.active
{
.t
hird-trend-switch
butto
n
.active
{
background
:
#2563eb
;
background
:
#2563eb
;
color
:
#fff
;
color
:
#fff
;
box-shadow
:
0
8px
16px
rgba
(
37
,
99
,
235
,
0.18
);
}
}
.trend-summary
svg
{
.third-trend-chart
{
display
:
block
;
min-height
:
390px
;
width
:
100%
;
padding
:
12px
14px
8px
;
height
:
220px
;
}
}
.trend-label
{
.third-trend-legend
{
fill
:
#64748b
;
display
:
flex
;
font-size
:
12px
;
gap
:
12px
;
overflow-x
:
auto
;
padding
:
2px
4px
10px
;
color
:
#475569
;
font-size
:
11px
;
font-weight
:
700
;
font-weight
:
700
;
white-space
:
nowrap
;
}
}
.t
rend-total-label
{
.t
hird-trend-legend
span
{
fill
:
#2563eb
;
display
:
inline-flex
;
font-size
:
12px
;
align-items
:
center
;
font-weight
:
800
;
gap
:
5px
;
}
}
.trend-hover-hit
{
.third-trend-legend
i
{
cursor
:
crosshair
;
width
:
8px
;
height
:
8px
;
border-radius
:
999px
;
}
}
.trend-tooltip
{
.third-trend-scroll
{
position
:
fixed
;
overflow-x
:
auto
;
z-index
:
30
;
overflow-y
:
hidden
;
display
:
none
;
}
min-width
:
260px
;
max-width
:
360px
;
.third-trend-scroll
svg
{
max-height
:
420px
;
display
:
block
;
overflow-y
:
auto
;
max-width
:
none
;
padding
:
10px
12px
;
}
border
:
1px
solid
rgba
(
148
,
163
,
184
,
0.28
);
.third-trend-label
{
fill
:
#64748b
;
font-size
:
11px
;
font-weight
:
700
;
}
.third-table-scroll
{
display
:
block
;
min-width
:
0
;
max-width
:
100%
;
width
:
100%
;
overflow-x
:
auto
;
}
.third-matrix-table
,
.third-detail-table
{
min-width
:
100%
;
white-space
:
nowrap
;
}
.third-matrix-table
th
,
.third-matrix-table
td
{
min-width
:
108px
;
text-align
:
center
;
}
.third-matrix-table
th
:first-child
,
.third-matrix-table
td
:first-child
{
position
:
sticky
;
left
:
0
;
z-index
:
2
;
min-width
:
150px
;
background
:
#fff
;
text-align
:
left
;
font-weight
:
800
;
}
.third-matrix-total
{
background
:
#f8fafc
;
font-weight
:
800
;
}
.third-empty
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
160px
;
color
:
#94a3b8
;
font-size
:
13px
;
}
.trend-chart
{
width
:
100%
;
min-height
:
300px
;
overflow-x
:
auto
;
border-radius
:
12px
;
background
:
#f8fafc
;
border
:
1px
solid
#edf2f7
;
}
.daily-stacked-legend
{
display
:
flex
;
justify-content
:
flex-end
;
gap
:
18px
;
padding
:
14px
16px
4px
;
color
:
#475569
;
font-size
:
13px
;
font-weight
:
700
;
}
.daily-source-filter
{
display
:
inline-flex
;
align-items
:
center
;
gap
:
7px
;
padding
:
7px
10px
;
border
:
1px
solid
transparent
;
border-radius
:
8px
;
background
:
transparent
;
color
:
#94a3b8
;
cursor
:
pointer
;
font
:
inherit
;
opacity
:
0.48
;
transition
:
opacity
0.18s
ease
,
background
0.18s
ease
,
border-color
0.18s
ease
;
}
.daily-source-filter.active
{
border-color
:
#dbe3ee
;
background
:
#fff
;
color
:
#334155
;
opacity
:
1
;
box-shadow
:
0
5px
14px
rgba
(
15
,
23
,
42
,
0.06
);
}
.daily-source-filter
:hover
{
opacity
:
1
;
}
.daily-stacked-legend
i
{
display
:
inline-block
;
width
:
10px
;
height
:
10px
;
border-radius
:
3px
;
}
.daily-stacked-grid
{
display
:
grid
;
grid-auto-flow
:
column
;
grid-auto-columns
:
minmax
(
430px
,
1
fr
);
gap
:
14px
;
overflow-x
:
auto
;
overflow-y
:
hidden
;
padding
:
10px
14px
14px
;
}
.daily-stacked-card
{
overflow
:
hidden
;
border
:
1px
solid
#e2e8f0
;
border-radius
:
13px
;
background
:
#fff
;
box-shadow
:
0
8px
22px
rgba
(
15
,
23
,
42
,
0.05
);
}
.daily-stacked-head
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
15px
16px
;
border-bottom
:
1px
solid
#e8edf4
;
}
.daily-stacked-head
strong
{
color
:
#0f172a
;
font-size
:
17px
;
font-weight
:
800
;
}
.daily-stacked-head
span
{
color
:
#64748b
;
font-size
:
13px
;
font-weight
:
800
;
}
.daily-stacked-list
{
display
:
grid
;
gap
:
13px
;
padding
:
14px
16px
17px
;
}
.daily-stacked-row
{
display
:
grid
;
grid-template-columns
:
minmax
(
142px
,
1.15
fr
)
minmax
(
130px
,
1
fr
)
54px
;
align-items
:
center
;
gap
:
12px
;
min-height
:
38px
;
}
.daily-stacked-name
{
overflow
:
hidden
;
color
:
#334155
;
font-size
:
13px
;
font-weight
:
800
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.daily-stacked-source-numbers
{
display
:
flex
;
gap
:
9px
;
margin-top
:
3px
;
color
:
#94a3b8
;
font-size
:
10px
;
font-weight
:
700
;
}
.daily-stacked-track
{
height
:
12px
;
overflow
:
hidden
;
border-radius
:
999px
;
background
:
#edf2f7
;
}
.daily-stacked-bar
{
display
:
flex
;
min-width
:
2px
;
height
:
100%
;
overflow
:
hidden
;
border-radius
:
inherit
;
}
.daily-stacked-system
{
height
:
100%
;
background
:
#4f6fd7
;
}
.daily-stacked-third-party
{
height
:
100%
;
background
:
#b4db2e
;
}
.daily-stacked-value
{
color
:
#0f172a
;
font-size
:
13px
;
font-weight
:
800
;
text-align
:
right
;
}
.chart-empty
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
220px
;
color
:
#6b7280
;
font-size
:
14px
;
}
.trend-summary
{
margin-top
:
16px
;
padding
:
16px
;
border
:
1px
solid
#edf2f7
;
border-radius
:
12px
;
background
:
linear-gradient
(
180deg
,
#ffffff
0%
,
#f8fafc
100%
);
}
.trend-summary-title
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
margin-bottom
:
12px
;
}
.trend-summary-title-main
{
color
:
#111827
;
font-size
:
16px
;
font-weight
:
800
;
}
.trend-summary-actions
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
}
.trend-summary-range
{
color
:
#64748b
;
font-size
:
13px
;
font-weight
:
700
;
}
.trend-switch
{
display
:
inline-grid
;
grid-template-columns
:
1
fr
1
fr
;
gap
:
2px
;
padding
:
3px
;
border
:
1px
solid
#d7dce3
;
border-radius
:
10px
;
background
:
#f1f5f9
;
}
.trend-switch-btn
{
border
:
none
;
border-radius
:
7px
;
background
:
transparent
;
color
:
#64748b
;
padding
:
6px
11px
;
cursor
:
pointer
;
font-size
:
13px
;
font-weight
:
800
;
}
.trend-switch-btn.active
{
background
:
#2563eb
;
color
:
#fff
;
box-shadow
:
0
8px
16px
rgba
(
37
,
99
,
235
,
0.18
);
}
.trend-summary
svg
{
display
:
block
;
width
:
100%
;
height
:
220px
;
}
.trend-label
{
fill
:
#64748b
;
font-size
:
12px
;
font-weight
:
700
;
}
.trend-total-label
{
fill
:
#2563eb
;
font-size
:
12px
;
font-weight
:
800
;
}
.trend-hover-hit
{
cursor
:
crosshair
;
}
.trend-tooltip
{
position
:
fixed
;
z-index
:
30
;
display
:
none
;
min-width
:
260px
;
max-width
:
360px
;
max-height
:
420px
;
overflow-y
:
auto
;
padding
:
10px
12px
;
border
:
1px
solid
rgba
(
148
,
163
,
184
,
0.28
);
border-radius
:
10px
;
border-radius
:
10px
;
background
:
rgba
(
15
,
23
,
42
,
0.94
);
background
:
rgba
(
15
,
23
,
42
,
0.94
);
color
:
#fff
;
color
:
#fff
;
...
@@ -594,6 +1115,28 @@
...
@@ -594,6 +1115,28 @@
font-size
:
14px
;
font-size
:
14px
;
}
}
@media
(
max-width
:
1500px
)
{
.third-metrics
{
grid-template-columns
:
repeat
(
3
,
minmax
(
0
,
1
fr
));
}
.third-panel
>
.filter-row
{
grid-template-columns
:
auto
repeat
(
2
,
minmax
(
180px
,
1
fr
));
}
.third-panel
>
.filter-row
.quick-range
{
grid-row
:
span
2
;
align-self
:
stretch
;
align-items
:
end
;
}
}
@media
(
max-width
:
1100px
)
{
.third-dashboard-grid
{
grid-template-columns
:
1
fr
;
}
}
@media
(
max-width
:
900px
)
{
@media
(
max-width
:
900px
)
{
.layout
{
.layout
{
grid-template-columns
:
1
fr
;
grid-template-columns
:
1
fr
;
...
@@ -615,6 +1158,23 @@
...
@@ -615,6 +1158,23 @@
grid-template-columns
:
repeat
(
2
,
1
fr
);
grid-template-columns
:
repeat
(
2
,
1
fr
);
}
}
.third-metrics
{
grid-template-columns
:
repeat
(
2
,
minmax
(
0
,
1
fr
));
}
.third-dashboard-grid
{
grid-template-columns
:
1
fr
;
}
.third-panel
>
.filter-row
{
grid-template-columns
:
repeat
(
2
,
minmax
(
0
,
1
fr
));
}
.third-panel
>
.filter-row
.quick-range
{
grid-row
:
auto
;
grid-column
:
1
/
-1
;
}
.header
{
.header
{
flex-direction
:
column
;
flex-direction
:
column
;
align-items
:
flex-start
;
align-items
:
flex-start
;
...
@@ -638,12 +1198,1134 @@
...
@@ -638,12 +1198,1134 @@
.field
,
.field
,
.field
input
,
.field
input
,
.field
select
,
.filter-row
.btn
,
.filter-row
.btn
,
.quick-range
{
.quick-range
{
width
:
100%
;
width
:
100%
;
}
}
.quick-btn
{
.third-metrics
{
flex
:
1
;
grid-template-columns
:
1
fr
;
}
.third-panel
>
.filter-row
{
grid-template-columns
:
1
fr
;
}
.third-panel
>
.filter-row
.quick-range
{
grid-column
:
auto
;
}
.quick-btn
{
flex
:
1
;
}
}
/* AIDSO violet theme */
:root
{
--aidso-primary
:
#7138f4
;
--aidso-primary-dark
:
#5824d6
;
--aidso-primary-soft
:
#f1eaff
;
--aidso-ink
:
#242332
;
--aidso-muted
:
#77778a
;
--aidso-line
:
#e8e5ef
;
--aidso-surface
:
rgba
(
255
,
255
,
255
,
0.98
);
--aidso-shadow
:
0
8px
24px
rgba
(
64
,
48
,
98
,
0.055
);
--aidso-shadow-hover
:
0
14px
32px
rgba
(
64
,
48
,
98
,
0.09
);
}
body
{
background
:
radial-gradient
(
circle
at
88%
0%
,
rgba
(
113
,
56
,
244
,
0.07
),
transparent
24%
),
#eef3ff
;
color
:
#282735
;
}
.sidebar
{
border-right
:
1px
solid
#e6e5ed
;
background
:
#fff
;
box-shadow
:
8px
0
24px
rgba
(
56
,
50
,
76
,
0.045
);
}
.brand
{
padding
:
10px
6px
;
border-color
:
transparent
;
background
:
#fff
;
box-shadow
:
none
;
}
.nav-label
{
color
:
#aaa7b5
;
}
.nav-btn
{
color
:
#646377
;
}
.nav-btn
::before
{
border-color
:
#d7d3df
;
color
:
#858194
;
}
.nav-btn
:hover
{
border-color
:
transparent
;
background
:
#f7f3ff
;
color
:
var
(
--aidso-primary
);
}
.nav-btn.active
{
border-color
:
#e4d8ff
;
background
:
linear-gradient
(
105deg
,
#eee5ff
,
#e7dcff
);
color
:
#6d2df0
;
box-shadow
:
0
8px
18px
rgba
(
113
,
56
,
244
,
0.12
);
}
.nav-btn.active
::after
{
background
:
#7138f4
;
}
.nav-btn.active
::before
{
border-color
:
#bda4fa
;
background
:
rgba
(
113
,
56
,
244
,
0.08
);
}
.sidebar-foot
{
border-top-color
:
#eeecf2
;
color
:
#92909e
;
}
.health-dot
{
background
:
#6fdaab
;
box-shadow
:
0
0
0
4px
rgba
(
111
,
218
,
171
,
0.14
);
}
.title
,
.section-title
,
.card-value
,
.third-metric-card
strong
{
color
:
#272533
;
}
.right
{
border-color
:
#e4e1ea
;
background
:
rgba
(
255
,
255
,
255
,
0.86
);
}
.btn
,
.quick-btn.active
{
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
box-shadow
:
0
7px
17px
rgba
(
113
,
56
,
244
,
0.22
);
}
.btn
:hover
,
.quick-btn.active
:hover
{
background
:
linear-gradient
(
135deg
,
#7138f4
,
#581bd4
);
box-shadow
:
0
10px
22px
rgba
(
113
,
56
,
244
,
0.27
);
}
.quick-btn
:hover
{
border-color
:
#e2d7fb
;
background
:
#fff
;
color
:
#7138f4
;
}
.card
,
.table-wrap
,
.trend-panel
,
.third-panel
{
border-color
:
#e4e4eb
;
}
.card
::after
,
.third-metric-card
::after
{
background
:
radial-gradient
(
circle
,
rgba
(
125
,
66
,
246
,
0.12
),
transparent
68%
);
}
.filter-row
{
border-color
:
#e7e4ee
;
background
:
linear-gradient
(
180deg
,
#faf9fc
,
#f7f6fa
);
}
.field
input
,
.field
select
,
.custom-select-trigger
{
border-color
:
#dcd9e3
;
}
.field
input
:focus
,
.field
select
:focus
,
.custom-select.open
.custom-select-trigger
,
.custom-select-trigger
:focus-visible
{
border-color
:
#7138f4
;
box-shadow
:
0
0
0
3px
rgba
(
113
,
56
,
244
,
0.13
);
}
.custom-select-option
:hover
{
background
:
#f3edff
;
color
:
#6425e4
;
}
.custom-select-option.selected
{
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
}
.trend-chart
{
border-color
:
#e8e5ef
;
background
:
linear-gradient
(
180deg
,
#fdfcff
,
#faf9fc
);
}
.daily-stacked-card
,
.third-metric-card
,
.third-section-card
{
border-color
:
#e7e3ec
;
}
.daily-stacked-system
,
.third-ranking-bar-inner
{
background
:
linear-gradient
(
90deg
,
#8b55f7
,
#7138f4
);
}
.daily-stacked-third-party
{
background
:
linear-gradient
(
90deg
,
#d3baff
,
#ae82ff
);
}
.third-ranking-index
{
background
:
#f1eaff
;
color
:
#7138f4
;
}
.third-trend-switch
button
.active
,
.trend-switch-btn.active
{
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
box-shadow
:
0
4px
10px
rgba
(
113
,
56
,
244
,
0.2
);
}
.daily-source-filter.active
{
border-color
:
#e2d9f5
;
color
:
#5d2bc6
;
}
.loading-box
{
border-color
:
#e5d9ff
;
color
:
#7138f4
;
}
.loading-spinner
{
border-color
:
#e2d4ff
;
border-top-color
:
#7138f4
;
}
@media
(
max-width
:
900px
)
{
.sidebar
{
border-bottom
:
1px
solid
#e7e4ed
;
background
:
rgba
(
255
,
255
,
255
,
0.97
);
}
}
.card
::after
,
.third-metric-card
::after
{
display
:
none
;
}
.logout-link
{
display
:
inline-flex
;
height
:
40px
;
align-items
:
center
;
justify-content
:
center
;
padding
:
0
12px
;
border-radius
:
9px
;
color
:
#77748a
;
font-size
:
13px
;
font-weight
:
700
;
text-decoration
:
none
;
transition
:
background
0.18s
ease
,
color
0.18s
ease
;
}
.logout-link
:hover
{
background
:
#f3edff
;
color
:
#6825ed
;
}
.brand
{
min-height
:
68px
;
padding
:
5px
2px
;
}
.brand-logo
{
width
:
100%
;
max-width
:
none
;
margin
:
0
;
transform
:
scale
(
1.28
);
transform-origin
:
center
;
}
@media
(
max-width
:
900px
)
{
.brand
{
min-height
:
42px
;
padding
:
4px
2px
;
}
.brand-logo
{
width
:
100%
;
margin
:
0
;
transform
:
scale
(
1.14
);
}
}
/* AIDSO unified visual system */
:root
{
--aidso-primary
:
#3157d5
;
--aidso-primary-dark
:
#2444b8
;
--aidso-primary-soft
:
#eef3ff
;
--aidso-ink
:
#14213d
;
--aidso-muted
:
#70809a
;
--aidso-line
:
#e2e8f2
;
--aidso-surface
:
rgba
(
255
,
255
,
255
,
0.96
);
--aidso-shadow
:
0
10px
32px
rgba
(
35
,
55
,
95
,
0.07
);
--aidso-shadow-hover
:
0
16px
40px
rgba
(
35
,
55
,
95
,
0.11
);
}
body
{
min-width
:
320px
;
background
:
radial-gradient
(
circle
at
92%
2%
,
rgba
(
79
,
111
,
215
,
0.11
),
transparent
27%
),
radial-gradient
(
circle
at
30%
100%
,
rgba
(
83
,
184
,
171
,
0.07
),
transparent
28%
),
#f4f7fb
;
color
:
var
(
--aidso-ink
);
letter-spacing
:
0.01em
;
}
.layout
{
grid-template-columns
:
242px
minmax
(
0
,
1
fr
);
}
.sidebar
{
position
:
sticky
;
top
:
0
;
z-index
:
30
;
display
:
flex
;
height
:
100vh
;
flex-direction
:
column
;
padding
:
24px
16px
18px
;
overflow-y
:
auto
;
border-right
:
1px
solid
rgba
(
255
,
255
,
255
,
0.06
);
background
:
radial-gradient
(
circle
at
10%
0%
,
rgba
(
81
,
113
,
224
,
0.24
),
transparent
31%
),
linear-gradient
(
180deg
,
#17233a
0%
,
#111a2c
100%
);
box-shadow
:
12px
0
36px
rgba
(
15
,
23
,
42
,
0.1
);
}
.brand
{
display
:
flex
;
align-items
:
center
;
min-height
:
58px
;
margin
:
0
6px
30px
;
padding
:
13px
14px
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.16
);
border-radius
:
14px
;
background
:
#fff
;
box-shadow
:
0
12px
28px
rgba
(
4
,
12
,
27
,
0.2
);
}
.brand-logo
{
display
:
block
;
width
:
100%
;
height
:
auto
;
object-fit
:
contain
;
}
.brand-mark
{
position
:
relative
;
display
:
grid
;
width
:
42px
;
height
:
42px
;
place-items
:
center
;
overflow
:
hidden
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.18
);
border-radius
:
13px
;
background
:
linear-gradient
(
145deg
,
#5679ee
,
#3157d5
);
box-shadow
:
0
10px
24px
rgba
(
49
,
87
,
213
,
0.35
);
}
.brand-mark
i
{
position
:
absolute
;
width
:
7px
;
border-radius
:
999px
;
background
:
#fff
;
transform
:
rotate
(
38deg
);
}
.brand-mark
i
:nth-child
(
1
)
{
height
:
16px
;
margin
:
8px
0
0
-14px
;
opacity
:
0.7
;
}
.brand-mark
i
:nth-child
(
2
)
{
height
:
25px
;
}
.brand-mark
i
:nth-child
(
3
)
{
height
:
33px
;
margin
:
-7px
-16px
0
0
;
}
.brand-copy
{
display
:
flex
;
flex-direction
:
column
;
gap
:
1px
;
color
:
#fff
;
}
.brand-copy
strong
{
font-size
:
18px
;
font-weight
:
850
;
letter-spacing
:
0.08em
;
}
.brand-copy
small
{
color
:
#aebbd2
;
font-size
:
12px
;
font-weight
:
650
;
letter-spacing
:
0.12em
;
}
.nav-label
{
margin
:
0
10px
10px
;
color
:
#71829f
;
font-size
:
10px
;
font-weight
:
800
;
letter-spacing
:
0.14em
;
}
.nav-list
{
display
:
grid
;
gap
:
7px
;
}
.nav-btn
{
position
:
relative
;
min-height
:
48px
;
margin
:
0
;
padding
:
0
14px
0
46px
;
overflow
:
hidden
;
border-radius
:
12px
;
background
:
transparent
;
color
:
#aebbd2
;
font-size
:
14px
;
font-weight
:
700
;
}
.nav-btn
::before
{
position
:
absolute
;
left
:
16px
;
display
:
grid
;
width
:
20px
;
height
:
20px
;
place-items
:
center
;
border
:
1px
solid
rgba
(
174
,
187
,
210
,
0.34
);
border-radius
:
6px
;
color
:
currentColor
;
font-size
:
11px
;
line-height
:
1
;
}
#queueNavBtn
::before
{
content
:
"Q"
;
}
#trendNavBtn
::before
{
content
:
"↗"
;
font-size
:
13px
;
}
#thirdNavBtn
::before
{
content
:
"3"
;
}
.nav-btn
:hover
{
border-color
:
transparent
;
background
:
rgba
(
255
,
255
,
255
,
0.06
);
color
:
#f8fafc
;
}
.nav-btn.active
{
border-color
:
rgba
(
128
,
155
,
255
,
0.24
);
background
:
linear-gradient
(
105deg
,
#3157d5
,
#4268df
);
color
:
#fff
;
box-shadow
:
0
10px
25px
rgba
(
28
,
56
,
159
,
0.32
);
}
.nav-btn.active
::after
{
content
:
""
;
position
:
absolute
;
top
:
12px
;
bottom
:
12px
;
left
:
0
;
width
:
3px
;
border-radius
:
0
4px
4px
0
;
background
:
#a9c1ff
;
}
.nav-btn.active
::before
{
border-color
:
rgba
(
255
,
255
,
255
,
0.45
);
background
:
rgba
(
255
,
255
,
255
,
0.1
);
}
.sidebar-foot
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
margin-top
:
auto
;
padding
:
14px
12px
4px
;
border-top
:
1px
solid
rgba
(
255
,
255
,
255
,
0.07
);
color
:
#8798b3
;
font-size
:
11px
;
font-weight
:
650
;
}
.health-dot
{
width
:
7px
;
height
:
7px
;
border-radius
:
50%
;
background
:
#37c99a
;
box-shadow
:
0
0
0
4px
rgba
(
55
,
201
,
154
,
0.12
);
}
.main
{
padding
:
28px
clamp
(
22px
,
2.5vw
,
42px
)
42px
;
background
:
transparent
;
}
.header
{
min-height
:
76px
;
margin-bottom
:
25px
;
}
.title
{
color
:
#111c34
;
font-size
:
clamp
(
25px
,
2vw
,
32px
);
font-weight
:
850
;
letter-spacing
:
-0.025em
;
}
.desc
{
margin-top
:
7px
;
color
:
var
(
--aidso-muted
);
font-size
:
13px
;
}
.right
{
padding
:
5px
5px
5px
14px
;
border
:
1px
solid
rgba
(
219
,
227
,
239
,
0.85
);
border-radius
:
13px
;
background
:
rgba
(
255
,
255
,
255
,
0.72
);
box-shadow
:
0
5px
18px
rgba
(
35
,
55
,
95
,
0.04
);
backdrop-filter
:
blur
(
10px
);
}
.last-time
,
.section-note
{
color
:
#7b8ba4
;
font-weight
:
600
;
}
.btn
,
.quick-btn
{
min-height
:
40px
;
border-radius
:
9px
;
background
:
linear-gradient
(
135deg
,
#3d64dd
,
#2850cd
);
box-shadow
:
0
7px
16px
rgba
(
49
,
87
,
213
,
0.2
);
}
.btn
:hover
,
.quick-btn
:hover
{
background
:
linear-gradient
(
135deg
,
#3157d5
,
#203fae
);
box-shadow
:
0
10px
20px
rgba
(
49
,
87
,
213
,
0.25
);
transform
:
translateY
(
-1px
);
}
.quick-btn
{
border
:
1px
solid
transparent
;
background
:
transparent
;
color
:
#63728a
;
box-shadow
:
none
;
}
.quick-btn
:hover
{
border-color
:
#d9e3f4
;
background
:
#fff
;
color
:
var
(
--aidso-primary
);
box-shadow
:
0
5px
14px
rgba
(
35
,
55
,
95
,
0.06
);
}
.quick-btn.active
{
border-color
:
transparent
;
background
:
linear-gradient
(
135deg
,
#4268df
,
#3157d5
);
box-shadow
:
0
8px
18px
rgba
(
49
,
87
,
213
,
0.22
);
}
.section-head
{
margin
:
12px
2px
13px
;
}
.section-title
{
color
:
#17233a
;
font-size
:
18px
;
font-weight
:
820
;
letter-spacing
:
-0.01em
;
}
.panel-card-head
{
min-height
:
54px
;
margin
:
-16px
-16px
16px
;
padding
:
0
18px
;
border-bottom
:
1px
solid
#e9e7ef
;
background
:
linear-gradient
(
180deg
,
#fff
,
#fcfbfe
);
}
.table-wrap
>
.panel-card-head
{
margin
:
0
;
}
.table-wrap
>
.panel-card-head
+
table
{
border-top
:
0
;
}
.summary
{
gap
:
14px
;
margin-bottom
:
26px
;
}
.card
,
.table-wrap
,
.trend-panel
,
.third-panel
{
border-color
:
rgba
(
220
,
228
,
240
,
0.9
);
border-radius
:
17px
;
background
:
var
(
--aidso-surface
);
box-shadow
:
var
(
--aidso-shadow
);
}
.card
{
position
:
relative
;
min-height
:
126px
;
padding
:
22px
;
overflow
:
hidden
;
transition
:
transform
0.2s
ease
,
box-shadow
0.2s
ease
;
}
.card
::after
,
.third-metric-card
::after
{
content
:
""
;
position
:
absolute
;
top
:
-28px
;
right
:
-25px
;
width
:
88px
;
height
:
88px
;
border-radius
:
50%
;
background
:
radial-gradient
(
circle
,
rgba
(
75
,
110
,
224
,
0.13
),
transparent
68%
);
pointer-events
:
none
;
}
.card
:hover
,
.third-metric-card
:hover
{
transform
:
translateY
(
-2px
);
box-shadow
:
var
(
--aidso-shadow-hover
);
}
.card-name
{
margin-bottom
:
13px
;
color
:
#72819a
;
font-size
:
13px
;
font-weight
:
700
;
}
.card-value
{
color
:
#13203a
;
font-size
:
32px
;
font-weight
:
850
;
letter-spacing
:
-0.035em
;
}
.table-wrap
{
overflow
:
auto
;
}
table
{
border-collapse
:
separate
;
border-spacing
:
0
;
}
th
{
border-bottom-color
:
#e8edf5
;
background
:
#f7f9fc
;
color
:
#62728c
;
font-size
:
12px
;
font-weight
:
800
;
letter-spacing
:
0.02em
;
}
td
{
border-bottom-color
:
#edf1f6
;
color
:
#26354e
;
font-size
:
13px
;
}
tbody
tr
{
transition
:
background
0.16s
ease
;
}
tbody
tr
:hover
{
background
:
#f7faff
;
}
tbody
tr
:last-child
td
{
border-bottom
:
0
;
}
.filter-row
{
gap
:
11px
;
padding
:
12px
;
border-color
:
#e6ebf3
;
border-radius
:
13px
;
background
:
linear-gradient
(
180deg
,
#f9fbfd
,
#f6f8fc
);
}
.field
{
gap
:
7px
;
}
.field
label
{
color
:
#75849b
;
font-size
:
12px
;
font-weight
:
750
;
}
.field
input
,
.field
select
,
.custom-select-trigger
{
border-color
:
#d8e0ec
;
border-radius
:
10px
;
color
:
#2b3a54
;
box-shadow
:
0
2px
5px
rgba
(
35
,
55
,
95
,
0.025
);
}
.custom-select-menu
{
padding
:
7px
;
border-color
:
#dce4f0
;
border-radius
:
13px
;
box-shadow
:
0
20px
48px
rgba
(
30
,
45
,
78
,
0.17
);
}
.custom-select-option
{
border-radius
:
8px
;
color
:
#3a4962
;
font-size
:
13px
;
}
.custom-select-option.selected
{
background
:
linear-gradient
(
135deg
,
#4268df
,
#3157d5
);
}
.trend-panel
,
.third-panel
{
padding
:
16px
;
}
.trend-chart
{
border-color
:
#e6ebf3
;
background
:
linear-gradient
(
180deg
,
#fbfcfe
,
#f7f9fc
);
}
.daily-stacked-card
,
.third-metric-card
,
.third-section-card
{
border-color
:
#e1e7f0
;
border-radius
:
14px
;
box-shadow
:
0
7px
20px
rgba
(
35
,
55
,
95
,
0.045
);
}
.daily-stacked-card
,
.third-section-card
{
transition
:
border-color
0.18s
ease
,
box-shadow
0.18s
ease
;
}
.daily-stacked-card
:hover
,
.third-section-card
:hover
{
border-color
:
#d3ddec
;
box-shadow
:
0
11px
28px
rgba
(
35
,
55
,
95
,
0.07
);
}
.daily-stacked-head
,
.third-card-head
{
min-height
:
55px
;
border-bottom-color
:
#e9eef5
;
background
:
linear-gradient
(
180deg
,
#fff
,
#fbfcfe
);
}
.daily-stacked-head
strong
,
.third-card-head
strong
{
color
:
#1b2942
;
}
.daily-stacked-track
,
.third-ranking-bar
{
background
:
#edf1f7
;
}
.daily-stacked-system
,
.third-ranking-bar-inner
{
background
:
linear-gradient
(
90deg
,
#5576df
,
#3157d5
);
}
.daily-stacked-third-party
{
background
:
linear-gradient
(
90deg
,
#b7d936
,
#9fc423
);
}
.third-metrics
{
gap
:
12px
;
}
.third-metric-card
{
position
:
relative
;
min-height
:
112px
;
padding
:
18px
;
overflow
:
hidden
;
transition
:
transform
0.2s
ease
,
box-shadow
0.2s
ease
;
}
.third-metric-card
span
{
color
:
#72819a
;
font-size
:
12px
;
}
.third-metric-card
strong
{
color
:
#14213d
;
font-weight
:
850
;
letter-spacing
:
-0.025em
;
}
.third-ranking-row
{
transition
:
background
0.15s
ease
,
transform
0.15s
ease
;
}
.third-ranking-row
:hover
{
background
:
#f4f7fd
;
transform
:
translateX
(
2px
);
}
.third-ranking-index
{
background
:
var
(
--aidso-primary-soft
);
color
:
var
(
--aidso-primary
);
}
.third-trend-switch
,
.trend-switch
{
border-color
:
#dce3ee
;
background
:
#f3f6fa
;
}
.third-trend-switch
button
.active
,
.trend-switch-btn.active
{
background
:
linear-gradient
(
135deg
,
#4268df
,
#3157d5
);
box-shadow
:
0
4px
10px
rgba
(
49
,
87
,
213
,
0.18
);
}
.third-table-scroll
{
scrollbar-color
:
#c5d0e2
transparent
;
scrollbar-width
:
thin
;
}
.third-table-scroll
::-webkit-scrollbar
,
.third-ranking-list
::-webkit-scrollbar
,
.third-trend-scroll
::-webkit-scrollbar
,
.daily-stacked-grid
::-webkit-scrollbar
{
width
:
7px
;
height
:
7px
;
}
.third-table-scroll
::-webkit-scrollbar-thumb
,
.third-ranking-list
::-webkit-scrollbar-thumb
,
.third-trend-scroll
::-webkit-scrollbar-thumb
,
.daily-stacked-grid
::-webkit-scrollbar-thumb
{
border-radius
:
999px
;
background
:
#cad4e3
;
}
.third-matrix-table
th
:first-child
,
.third-matrix-table
td
:first-child
{
background
:
#fff
;
box-shadow
:
1px
0
0
#e8edf5
;
}
.third-matrix-table
thead
th
:first-child
{
background
:
#f7f9fc
;
}
.status
{
border-radius
:
999px
;
font-size
:
11px
;
font-weight
:
800
;
}
.loading-box
{
border-color
:
#d9e4fb
;
border-radius
:
13px
;
color
:
var
(
--aidso-primary
);
box-shadow
:
0
16px
36px
rgba
(
35
,
55
,
95
,
0.12
);
}
.trend-loading-mask
{
border-radius
:
17px
;
background
:
rgba
(
247
,
249
,
253
,
0.78
);
backdrop-filter
:
blur
(
5px
);
}
.error
{
border
:
1px
solid
#fecaca
;
border-radius
:
11px
;
box-shadow
:
0
8px
20px
rgba
(
153
,
27
,
27
,
0.06
);
}
@media
(
max-width
:
900px
)
{
.layout
{
grid-template-columns
:
1
fr
;
}
.sidebar
{
position
:
static
;
display
:
grid
;
height
:
auto
;
grid-template-columns
:
auto
1
fr
;
align-items
:
center
;
padding
:
12px
14px
;
}
.brand
{
width
:
142px
;
min-height
:
40px
;
margin
:
0
16px
0
0
;
padding
:
8px
10px
;
border-radius
:
10px
;
}
.brand-mark
{
width
:
36px
;
height
:
36px
;
border-radius
:
11px
;
}
.brand-copy
small
,
.nav-label
,
.sidebar-foot
{
display
:
none
;
}
.nav-list
{
display
:
flex
;
justify-content
:
flex-end
;
gap
:
7px
;
}
.nav-btn
{
width
:
auto
;
min-height
:
40px
;
padding
:
0
12px
;
white-space
:
nowrap
;
}
.nav-btn
::before
,
.nav-btn.active
::after
{
display
:
none
;
}
.main
{
padding-top
:
22px
;
}
}
@media
(
max-width
:
600px
)
{
.sidebar
{
grid-template-columns
:
1
fr
;
gap
:
10px
;
}
.brand
{
width
:
154px
;
justify-content
:
center
;
margin-right
:
0
;
justify-self
:
center
;
}
.nav-list
{
display
:
grid
;
grid-template-columns
:
repeat
(
3
,
minmax
(
0
,
1
fr
));
}
.nav-btn
{
justify-content
:
center
;
padding
:
0
8px
;
font-size
:
12px
;
}
.header
{
align-items
:
stretch
;
}
.right
{
justify-content
:
space-between
;
}
.last-time
{
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
}
/* Final AIDSO palette overrides */
:root
{
--aidso-primary
:
#7138f4
;
--aidso-primary-dark
:
#5824d6
;
--aidso-primary-soft
:
#f1eaff
;
--aidso-ink
:
#282735
;
--aidso-muted
:
#77778a
;
--aidso-line
:
#e8e5ef
;
--aidso-shadow
:
0
8px
24px
rgba
(
64
,
48
,
98
,
0.055
);
--aidso-shadow-hover
:
0
14px
32px
rgba
(
64
,
48
,
98
,
0.09
);
}
body
{
background
:
radial-gradient
(
circle
at
88%
0%
,
rgba
(
113
,
56
,
244
,
0.07
),
transparent
24%
),
#eef3ff
;
}
.sidebar
{
border-right-color
:
#e6e5ed
;
background
:
#fff
;
box-shadow
:
8px
0
24px
rgba
(
56
,
50
,
76
,
0.045
);
}
.brand
{
border-color
:
transparent
;
background
:
#fff
;
box-shadow
:
none
;
}
.nav-label
{
color
:
#aaa7b5
;
}
.nav-btn
{
color
:
#646377
;
}
.nav-btn
::before
{
border-color
:
#d7d3df
;
color
:
#858194
;
}
.nav-btn
:hover
{
background
:
#f7f3ff
;
color
:
#7138f4
;
}
.nav-btn.active
{
border-color
:
#e4d8ff
;
background
:
linear-gradient
(
105deg
,
#eee5ff
,
#e7dcff
);
color
:
#6d2df0
;
box-shadow
:
0
8px
18px
rgba
(
113
,
56
,
244
,
0.12
);
}
.nav-btn.active
::after
{
background
:
#7138f4
;
}
.nav-btn.active
::before
{
border-color
:
#bda4fa
;
background
:
rgba
(
113
,
56
,
244
,
0.08
);
}
.sidebar-foot
{
border-top-color
:
#eeecf2
;
color
:
#92909e
;
}
.btn
,
.quick-btn.active
{
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
box-shadow
:
0
7px
17px
rgba
(
113
,
56
,
244
,
0.22
);
}
.btn
:hover
,
.quick-btn.active
:hover
{
background
:
linear-gradient
(
135deg
,
#7138f4
,
#581bd4
);
box-shadow
:
0
10px
22px
rgba
(
113
,
56
,
244
,
0.27
);
}
.quick-btn
:hover
{
border-color
:
#e2d7fb
;
color
:
#7138f4
;
}
.card
::after
,
.third-metric-card
::after
{
background
:
radial-gradient
(
circle
,
rgba
(
125
,
66
,
246
,
0.12
),
transparent
68%
);
}
.filter-row
{
border-color
:
#e7e4ee
;
background
:
linear-gradient
(
180deg
,
#faf9fc
,
#f7f6fa
);
}
.field
input
:focus
,
.field
select
:focus
,
.custom-select.open
.custom-select-trigger
,
.custom-select-trigger
:focus-visible
{
border-color
:
#7138f4
;
box-shadow
:
0
0
0
3px
rgba
(
113
,
56
,
244
,
0.13
);
}
.custom-select-option
:hover
{
background
:
#f3edff
;
color
:
#6425e4
;
}
.custom-select-option.selected
,
.third-trend-switch
button
.active
,
.trend-switch-btn.active
{
background
:
linear-gradient
(
135deg
,
#8247ff
,
#6825ed
);
}
.daily-stacked-system
,
.third-ranking-bar-inner
{
background
:
linear-gradient
(
90deg
,
#8b55f7
,
#7138f4
);
}
.daily-stacked-third-party
{
background
:
linear-gradient
(
90deg
,
#d3baff
,
#ae82ff
);
}
.third-ranking-index
{
background
:
#f1eaff
;
color
:
#7138f4
;
}
.loading-box
{
border-color
:
#e5d9ff
;
color
:
#7138f4
;
}
.loading-spinner
{
border-color
:
#e2d4ff
;
border-top-color
:
#7138f4
;
}
@media
(
max-width
:
900px
)
{
.sidebar
{
border-bottom
:
1px
solid
#e7e4ed
;
background
:
rgba
(
255
,
255
,
255
,
0.97
);
}
}
}
}
aidso_geo/core/static/dashboard/queue_monitor.js
View file @
27ac512a
const
API_URL
=
"/api/queue/status"
;
const
API_URL
=
"/api/queue/status"
;
const
STATS_API_URL
=
"/api/queue/platformStats"
;
const
STATS_API_URL
=
"/api/queue/platformStats"
;
const
THIRD_STATS_API_URL
=
"/api/queue/thirdPartyStats"
;
const
platforms
=
[
const
platforms
=
[
"BDAI"
,
"DB"
,
"DOUBA"
,
"DP"
,
"DPA"
,
"DYAI"
,
"BDAI"
,
"DB"
,
"DOUBA"
,
"DP"
,
"DPA"
,
"DYAI"
,
"KIMI"
,
"TXYB"
,
"TXYBA"
,
"TYQW"
,
"TYQWA"
,
"WXYY"
,
"XHSA"
"KIMI"
,
"TXYB"
,
"TXYBA"
,
"TYQW"
,
"TYQWA"
,
"WXYY"
,
"XHSA"
];
];
const
PLATFORM_NAME_MAP
=
{
const
PLATFORM_NAME_MAP
=
{
XHSA
:
"小红书手机版"
DOUBA
:
"豆包·手机版"
,
DB
:
"豆包·网页版"
,
DPA
:
"DeepSeek·手机版"
,
DP
:
"DeepSeek·网页版"
,
TYQWA
:
"千问·手机版"
,
TYQW
:
"千问·网页版"
,
TXYBA
:
"腾讯元宝·手机版"
,
TXYB
:
"腾讯元宝·网页版"
,
KIMI
:
"KIMI·网页版"
,
WXYY
:
"文心·网页版"
,
DYAI
:
"AI抖音·网页版"
,
BDAI
:
"百度AI·网页版"
,
XHSA
:
"红书问一问"
};
};
const
PLATFORM_DISPLAY_ORDER
=
[
"豆包·手机版"
,
"豆包·网页版"
,
"千问·手机版"
,
"千问·网页版"
,
"DeepSeek·手机版"
,
"DeepSeek·网页版"
,
"腾讯元宝·手机版"
,
"腾讯元宝·网页版"
,
"KIMI·网页版"
,
"文心·网页版"
,
"百度AI·网页版"
,
"红书问一问"
,
"AI抖音·网页版"
];
let
currentPanel
=
"queue"
;
let
currentPanel
=
"queue"
;
let
loadedPanels
=
{
queue
:
false
,
trend
:
false
};
let
loadedPanels
=
{
queue
:
false
,
trend
:
false
,
third
:
false
};
let
trendChartMode
=
"platform"
;
let
trendChartMode
=
"platform"
;
let
dailySourceMode
=
"all"
;
let
lastTrendData
=
null
;
let
lastTrendData
=
null
;
let
thirdTrendMode
=
"channel"
;
let
lastThirdData
=
null
;
function
showPanel
(
panel
)
{
function
showPanel
(
panel
)
{
currentPanel
=
panel
;
currentPanel
=
panel
;
document
.
getElementById
(
"queuePanel"
).
classList
.
toggle
(
"active"
,
panel
===
"queue"
);
document
.
getElementById
(
"queuePanel"
).
classList
.
toggle
(
"active"
,
panel
===
"queue"
);
document
.
getElementById
(
"trendPanel"
).
classList
.
toggle
(
"active"
,
panel
===
"trend"
);
document
.
getElementById
(
"trendPanel"
).
classList
.
toggle
(
"active"
,
panel
===
"trend"
);
document
.
getElementById
(
"thirdPanel"
).
classList
.
toggle
(
"active"
,
panel
===
"third"
);
document
.
getElementById
(
"queueNavBtn"
).
classList
.
toggle
(
"active"
,
panel
===
"queue"
);
document
.
getElementById
(
"queueNavBtn"
).
classList
.
toggle
(
"active"
,
panel
===
"queue"
);
document
.
getElementById
(
"trendNavBtn"
).
classList
.
toggle
(
"active"
,
panel
===
"trend"
);
document
.
getElementById
(
"trendNavBtn"
).
classList
.
toggle
(
"active"
,
panel
===
"trend"
);
document
.
getElementById
(
"pageTitle"
).
innerText
=
panel
===
"queue"
document
.
getElementById
(
"thirdNavBtn"
).
classList
.
toggle
(
"active"
,
panel
===
"third"
);
?
"GEO 队列监控面板"
const
pageMeta
=
{
:
"GEO历史任务数量趋势"
;
queue
:
{
document
.
getElementById
(
"pageDesc"
).
innerText
=
panel
===
"queue"
title
:
"队列积压监控"
,
?
"查看各平台 stream_batch / batch 队列积压情况"
desc
:
"查看各平台 stream_batch / batch 队列积压情况"
:
"按时间范围查看各平台历史任务趋势"
;
},
trend
:
{
title
:
"GEO历史任务数量趋势"
,
desc
:
"按时间范围查看各平台历史任务趋势"
},
third
:
{
title
:
"三方用量详细分析"
,
desc
:
"查看当前启用渠道的任务量、平台分布与每日趋势"
}
};
document
.
getElementById
(
"pageTitle"
).
innerText
=
pageMeta
[
panel
].
title
;
document
.
getElementById
(
"pageDesc"
).
innerText
=
pageMeta
[
panel
].
desc
;
hideError
();
hideError
();
if
(
!
loadedPanels
[
panel
])
{
if
(
!
loadedPanels
[
panel
])
{
...
@@ -34,8 +77,10 @@ const API_URL = "/api/queue/status";
...
@@ -34,8 +77,10 @@ const API_URL = "/api/queue/status";
function
refreshCurrentPanel
()
{
function
refreshCurrentPanel
()
{
if
(
currentPanel
===
"queue"
)
{
if
(
currentPanel
===
"queue"
)
{
loadQueueStatus
();
loadQueueStatus
();
}
else
{
}
else
if
(
currentPanel
===
"trend"
)
{
loadPlatformStats
();
loadPlatformStats
();
}
else
{
loadThirdPartyStats
();
}
}
}
}
...
@@ -55,6 +100,10 @@ const API_URL = "/api/queue/status";
...
@@ -55,6 +100,10 @@ const API_URL = "/api/queue/status";
document
.
querySelector
(
".trend-panel"
).
classList
.
toggle
(
"loading"
,
isLoading
);
document
.
querySelector
(
".trend-panel"
).
classList
.
toggle
(
"loading"
,
isLoading
);
}
}
function
setThirdLoading
(
isLoading
)
{
document
.
querySelector
(
".third-panel"
).
classList
.
toggle
(
"loading"
,
isLoading
);
}
function
getNumClass
(
num
)
{
function
getNumClass
(
num
)
{
if
(
num
===
0
)
{
if
(
num
===
0
)
{
return
"num-ok"
;
return
"num-ok"
;
...
@@ -127,9 +176,16 @@ const API_URL = "/api/queue/status";
...
@@ -127,9 +176,16 @@ const API_URL = "/api/queue/status";
.
map
(
item
=>
({
.
map
(
item
=>
({
time
:
normalizeStatDate
(
item
[
"时间"
]
||
item
.
time
||
item
.
date
||
item
.
begin
||
""
),
time
:
normalizeStatDate
(
item
[
"时间"
]
||
item
.
time
||
item
.
date
||
item
.
begin
||
""
),
platform
:
getPlatformDisplayName
(
item
[
"平台"
]
||
item
.
platform
||
item
.
name
||
"未知平台"
),
platform
:
getPlatformDisplayName
(
item
[
"平台"
]
||
item
.
platform
||
item
.
name
||
"未知平台"
),
count
:
Number
(
item
[
"数量"
]
||
item
.
count
||
item
.
value
||
0
)
count
:
Number
(
item
[
"数量"
]
||
item
.
count
||
item
.
value
||
0
),
systemCount
:
Number
(
item
.
system_count
||
0
),
thirdPartyCount
:
Number
(
item
.
third_party_count
||
0
)
}))
}))
.
filter
(
item
=>
item
.
time
&&
Number
.
isFinite
(
item
.
count
));
.
filter
(
item
=>
(
item
.
time
&&
Number
.
isFinite
(
item
.
count
)
&&
Number
.
isFinite
(
item
.
systemCount
)
&&
Number
.
isFinite
(
item
.
thirdPartyCount
)
));
}
}
function
getPlatformDisplayName
(
platform
)
{
function
getPlatformDisplayName
(
platform
)
{
...
@@ -146,11 +202,178 @@ const API_URL = "/api/queue/status";
...
@@ -146,11 +202,178 @@ const API_URL = "/api/queue/status";
return
el
;
return
el
;
}
}
function
renderStackedPlatformChart
(
chart
,
labels
,
platformList
,
sourcePointMap
)
{
const
systemColor
=
"#7138f4"
;
const
thirdPartyColor
=
"#b4db2e"
;
const
getDisplayValue
=
source
=>
{
const
systemCount
=
Number
(
source
.
systemCount
||
0
);
const
thirdPartyCount
=
Number
(
source
.
thirdPartyCount
||
0
);
if
(
dailySourceMode
===
"system"
)
{
return
systemCount
;
}
if
(
dailySourceMode
===
"thirdParty"
)
{
return
thirdPartyCount
;
}
return
systemCount
+
thirdPartyCount
;
};
const
maxValue
=
Math
.
max
(
...
platformList
.
flatMap
(
platform
=>
labels
.
map
(
label
=>
{
const
source
=
sourcePointMap
.
get
(
`
${
platform
}
__
${
label
}
`
)
||
{};
return
getDisplayValue
(
source
);
})),
1
);
const
grid
=
document
.
createElement
(
"div"
);
const
legend
=
document
.
createElement
(
"div"
);
const
systemButton
=
document
.
createElement
(
"button"
);
const
thirdPartyButton
=
document
.
createElement
(
"button"
);
grid
.
className
=
"daily-stacked-grid"
;
legend
.
className
=
"daily-stacked-legend"
;
systemButton
.
className
=
(
"daily-source-filter "
+
(
dailySourceMode
!==
"thirdParty"
?
"active"
:
""
)
);
thirdPartyButton
.
className
=
(
"daily-source-filter "
+
(
dailySourceMode
!==
"system"
?
"active"
:
""
)
);
systemButton
.
innerHTML
=
(
`<i style="background:
${
systemColor
}
"></i>系统用户`
);
thirdPartyButton
.
innerHTML
=
(
`<i style="background:
${
thirdPartyColor
}
"></i>三方用户`
);
systemButton
.
onclick
=
()
=>
setDailySourceMode
(
"system"
);
thirdPartyButton
.
onclick
=
()
=>
setDailySourceMode
(
"thirdParty"
);
legend
.
appendChild
(
systemButton
);
legend
.
appendChild
(
thirdPartyButton
);
chart
.
appendChild
(
legend
);
[...
labels
].
reverse
().
forEach
(
label
=>
{
const
card
=
document
.
createElement
(
"section"
);
const
head
=
document
.
createElement
(
"div"
);
const
date
=
document
.
createElement
(
"strong"
);
const
total
=
document
.
createElement
(
"span"
);
const
list
=
document
.
createElement
(
"div"
);
const
dayTotal
=
platformList
.
reduce
((
sum
,
platform
)
=>
{
const
source
=
sourcePointMap
.
get
(
`
${
platform
}
__
${
label
}
`
)
||
{};
return
sum
+
getDisplayValue
(
source
);
},
0
);
card
.
className
=
"daily-stacked-card"
;
head
.
className
=
"daily-stacked-head"
;
date
.
textContent
=
label
;
total
.
textContent
=
`总量
${
dayTotal
}
`
;
list
.
className
=
"daily-stacked-list"
;
head
.
appendChild
(
date
);
head
.
appendChild
(
total
);
card
.
appendChild
(
head
);
platformList
.
forEach
(
platform
=>
{
const
source
=
sourcePointMap
.
get
(
`
${
platform
}
__
${
label
}
`
)
||
{};
const
systemCount
=
Number
(
source
.
systemCount
||
0
);
const
thirdPartyCount
=
Number
(
source
.
thirdPartyCount
||
0
);
const
rowTotal
=
getDisplayValue
(
source
);
const
row
=
document
.
createElement
(
"div"
);
const
nameWrap
=
document
.
createElement
(
"div"
);
const
name
=
document
.
createElement
(
"div"
);
const
sourceNumbers
=
document
.
createElement
(
"div"
);
const
track
=
document
.
createElement
(
"div"
);
const
stack
=
document
.
createElement
(
"div"
);
const
systemBar
=
document
.
createElement
(
"span"
);
const
thirdPartyBar
=
document
.
createElement
(
"span"
);
const
value
=
document
.
createElement
(
"strong"
);
const
totalWidth
=
(
rowTotal
/
maxValue
)
*
100
;
const
systemWidth
=
dailySourceMode
===
"thirdParty"
?
0
:
(
dailySourceMode
===
"system"
?
100
:
(
rowTotal
?
(
systemCount
/
rowTotal
)
*
100
:
0
)
);
const
thirdPartyWidth
=
dailySourceMode
===
"system"
?
0
:
(
dailySourceMode
===
"thirdParty"
?
100
:
(
rowTotal
?
(
thirdPartyCount
/
rowTotal
)
*
100
:
0
)
);
row
.
className
=
"daily-stacked-row"
;
nameWrap
.
className
=
"daily-stacked-name-wrap"
;
name
.
className
=
"daily-stacked-name"
;
sourceNumbers
.
className
=
"daily-stacked-source-numbers"
;
track
.
className
=
"daily-stacked-track"
;
stack
.
className
=
"daily-stacked-bar"
;
systemBar
.
className
=
"daily-stacked-system"
;
thirdPartyBar
.
className
=
"daily-stacked-third-party"
;
value
.
className
=
"daily-stacked-value"
;
name
.
textContent
=
platform
;
if
(
dailySourceMode
===
"system"
)
{
sourceNumbers
.
innerHTML
=
(
`<span>系统
${
systemCount
}
</span>`
);
}
else
if
(
dailySourceMode
===
"thirdParty"
)
{
sourceNumbers
.
innerHTML
=
(
`<span>三方
${
thirdPartyCount
}
</span>`
);
}
else
{
sourceNumbers
.
innerHTML
=
`
<span>系统
${
systemCount
}
</span>
<span>三方
${
thirdPartyCount
}
</span>
`
;
}
stack
.
style
.
width
=
`
${
totalWidth
}
%`
;
systemBar
.
style
.
width
=
`
${
systemWidth
}
%`
;
thirdPartyBar
.
style
.
width
=
`
${
thirdPartyWidth
}
%`
;
stack
.
title
=
(
`
${
platform
}
${
label
}
:`
+
`系统用户
${
systemCount
}
,`
+
`三方用户
${
thirdPartyCount
}
,`
+
`合计
${
rowTotal
}
`
);
value
.
textContent
=
rowTotal
;
nameWrap
.
appendChild
(
name
);
nameWrap
.
appendChild
(
sourceNumbers
);
stack
.
appendChild
(
systemBar
);
stack
.
appendChild
(
thirdPartyBar
);
track
.
appendChild
(
stack
);
row
.
appendChild
(
nameWrap
);
row
.
appendChild
(
track
);
row
.
appendChild
(
value
);
list
.
appendChild
(
row
);
});
card
.
appendChild
(
list
);
grid
.
appendChild
(
card
);
});
chart
.
appendChild
(
grid
);
}
function
setDailySourceMode
(
mode
)
{
dailySourceMode
=
dailySourceMode
===
mode
?
"all"
:
mode
;
if
(
!
lastTrendData
)
{
return
;
}
const
chart
=
document
.
getElementById
(
"platformTrendChart"
);
chart
.
innerHTML
=
""
;
renderStackedPlatformChart
(
chart
,
lastTrendData
.
labels
,
lastTrendData
.
platformList
,
lastTrendData
.
sourcePointMap
);
}
function
renderPlatformTrend
(
responseData
)
{
function
renderPlatformTrend
(
responseData
)
{
const
chart
=
document
.
getElementById
(
"platformTrendChart"
);
const
chart
=
document
.
getElementById
(
"platformTrendChart"
);
const
summary
=
document
.
getElementById
(
"trendSummary"
);
const
summary
=
document
.
getElementById
(
"trendSummary"
);
const
statsNote
=
document
.
getElementById
(
"statsNote"
);
const
statsNote
=
document
.
getElementById
(
"statsNote"
);
const
colors
=
[
"#
2563eb"
,
"#16a34a"
,
"#f59e0b"
,
"#dc2626"
,
"#7c3aed
"
,
"#0891b2"
,
"#db2777"
,
"#475569"
];
const
colors
=
[
"#
7138f4"
,
"#16a34a"
,
"#f59e0b"
,
"#dc2626"
,
"#9b5cff
"
,
"#0891b2"
,
"#db2777"
,
"#475569"
];
const
items
=
normalizeStats
(
responseData
);
const
items
=
normalizeStats
(
responseData
);
chart
.
innerHTML
=
""
;
chart
.
innerHTML
=
""
;
...
@@ -163,22 +386,42 @@ const API_URL = "/api/queue/status";
...
@@ -163,22 +386,42 @@ const API_URL = "/api/queue/status";
}
}
const
chartLabels
=
[...
new
Set
(
items
.
map
(
item
=>
item
.
time
))].
sort
();
const
chartLabels
=
[...
new
Set
(
items
.
map
(
item
=>
item
.
time
))].
sort
();
const
cardLabels
=
[...
chartLabels
].
reverse
();
const
oldestDate
=
chartLabels
[
0
];
const
oldestDate
=
chartLabels
[
0
];
const
newestDate
=
chartLabels
[
chartLabels
.
length
-
1
];
const
newestDate
=
chartLabels
[
chartLabels
.
length
-
1
];
const
platformTotals
=
new
Map
();
const
platformTotals
=
new
Map
();
const
pointMap
=
new
Map
();
const
pointMap
=
new
Map
();
const
sourcePointMap
=
new
Map
();
items
.
forEach
(
item
=>
{
items
.
forEach
(
item
=>
{
platformTotals
.
set
(
item
.
platform
,
(
platformTotals
.
get
(
item
.
platform
)
||
0
)
+
item
.
count
);
platformTotals
.
set
(
item
.
platform
,
(
platformTotals
.
get
(
item
.
platform
)
||
0
)
+
item
.
count
);
const
pointKey
=
`
${
item
.
platform
}
__
${
item
.
time
}
`
;
const
pointKey
=
`
${
item
.
platform
}
__
${
item
.
time
}
`
;
pointMap
.
set
(
pointKey
,
(
pointMap
.
get
(
pointKey
)
||
0
)
+
item
.
count
);
pointMap
.
set
(
pointKey
,
(
pointMap
.
get
(
pointKey
)
||
0
)
+
item
.
count
);
const
sourcePoint
=
sourcePointMap
.
get
(
pointKey
)
||
{
systemCount
:
0
,
thirdPartyCount
:
0
};
sourcePoint
.
systemCount
+=
item
.
systemCount
;
sourcePoint
.
thirdPartyCount
+=
item
.
thirdPartyCount
;
sourcePointMap
.
set
(
pointKey
,
sourcePoint
);
});
});
const
platformList
=
[...
platformTotals
.
entries
()]
const
platformList
=
[...
platformTotals
.
entries
()]
.
sort
((
a
,
b
)
=>
b
[
1
]
-
a
[
1
])
.
sort
((
a
,
b
)
=>
{
const
aIndex
=
PLATFORM_DISPLAY_ORDER
.
indexOf
(
a
[
0
]);
const
bIndex
=
PLATFORM_DISPLAY_ORDER
.
indexOf
(
b
[
0
]);
const
normalizedAIndex
=
aIndex
===
-
1
?
Number
.
MAX_SAFE_INTEGER
:
aIndex
;
const
normalizedBIndex
=
bIndex
===
-
1
?
Number
.
MAX_SAFE_INTEGER
:
bIndex
;
if
(
normalizedAIndex
!==
normalizedBIndex
)
{
return
normalizedAIndex
-
normalizedBIndex
;
}
return
b
[
1
]
-
a
[
1
];
})
.
map
(([
platform
])
=>
platform
);
.
map
(([
platform
])
=>
platform
);
const
maxCount
=
Math
.
max
(...
pointMap
.
values
(),
1
);
const
platformColorMap
=
new
Map
(
platformList
.
map
((
platform
,
index
)
=>
[
platform
,
colors
[
index
%
colors
.
length
]]));
const
platformColorMap
=
new
Map
(
platformList
.
map
((
platform
,
index
)
=>
[
platform
,
colors
[
index
%
colors
.
length
]]));
const
dayTotals
=
chartLabels
.
map
(
label
=>
({
const
dayTotals
=
chartLabels
.
map
(
label
=>
({
date
:
label
,
date
:
label
,
...
@@ -189,65 +432,17 @@ const API_URL = "/api/queue/status";
...
@@ -189,65 +432,17 @@ const API_URL = "/api/queue/status";
labels
:
chartLabels
,
labels
:
chartLabels
,
platformList
,
platformList
,
pointMap
,
pointMap
,
sourcePointMap
,
platformColorMap
,
platformColorMap
,
dayTotals
dayTotals
};
};
const
grid
=
document
.
createElement
(
"div"
);
renderStackedPlatformChart
(
grid
.
className
=
"daily-grid"
;
chart
,
chartLabels
,
cardLabels
.
forEach
(
label
=>
{
platformList
,
const
dayItems
=
platformList
.
map
(
platform
=>
({
sourcePointMap
platform
,
);
value
:
pointMap
.
get
(
`
${
platform
}
__
${
label
}
`
)
||
0
,
color
:
platformColorMap
.
get
(
platform
)
}));
const
dayTotal
=
dayItems
.
reduce
((
sum
,
item
)
=>
sum
+
item
.
value
,
0
);
const
card
=
document
.
createElement
(
"div"
);
const
head
=
document
.
createElement
(
"div"
);
const
date
=
document
.
createElement
(
"div"
);
const
total
=
document
.
createElement
(
"div"
);
const
list
=
document
.
createElement
(
"div"
);
card
.
className
=
"day-card"
;
head
.
className
=
"day-card-head"
;
date
.
className
=
"day-date"
;
total
.
className
=
"day-total"
;
list
.
className
=
"day-list"
;
date
.
innerText
=
label
;
total
.
innerText
=
`总量
${
dayTotal
}
`
;
head
.
appendChild
(
date
);
head
.
appendChild
(
total
);
dayItems
.
forEach
(
item
=>
{
const
row
=
document
.
createElement
(
"div"
);
const
name
=
document
.
createElement
(
"div"
);
const
bar
=
document
.
createElement
(
"div"
);
const
barInner
=
document
.
createElement
(
"div"
);
const
count
=
document
.
createElement
(
"div"
);
row
.
className
=
"day-row"
;
name
.
className
=
"day-platform"
;
bar
.
className
=
"day-bar"
;
barInner
.
className
=
"day-bar-inner"
;
count
.
className
=
"day-count"
;
name
.
innerText
=
item
.
platform
;
barInner
.
style
.
width
=
`
${
Math
.
max
(
2
,
Math
.
round
((
item
.
value
/
maxCount
)
*
100
))}
%`
;
barInner
.
style
.
background
=
item
.
color
;
count
.
innerText
=
item
.
value
;
bar
.
appendChild
(
barInner
);
row
.
appendChild
(
name
);
row
.
appendChild
(
bar
);
row
.
appendChild
(
count
);
list
.
appendChild
(
row
);
});
card
.
appendChild
(
head
);
card
.
appendChild
(
list
);
grid
.
appendChild
(
card
);
});
chart
.
appendChild
(
grid
);
renderTrendSummary
();
renderTrendSummary
();
statsNote
.
innerText
=
`共
${
platformList
.
length
}
个平台,
${
oldestDate
}
至
${
newestDate
}
`
;
statsNote
.
innerText
=
`共
${
platformList
.
length
}
个平台,
${
oldestDate
}
至
${
newestDate
}
`
;
document
.
getElementById
(
"lastTime"
).
innerText
=
"最后刷新:"
+
new
Date
().
toLocaleString
();
document
.
getElementById
(
"lastTime"
).
innerText
=
"最后刷新:"
+
new
Date
().
toLocaleString
();
...
@@ -263,7 +458,7 @@ const API_URL = "/api/queue/status";
...
@@ -263,7 +458,7 @@ const API_URL = "/api/queue/status";
const
series
=
trendChartMode
===
"total"
const
series
=
trendChartMode
===
"total"
?
[{
?
[{
name
:
"总量"
,
name
:
"总量"
,
color
:
"#
2563eb
"
,
color
:
"#
7138f4
"
,
values
:
lastTrendData
.
dayTotals
.
map
(
item
=>
item
.
total
)
values
:
lastTrendData
.
dayTotals
.
map
(
item
=>
item
.
total
)
}]
}]
:
lastTrendData
.
platformList
.
map
(
platform
=>
({
:
lastTrendData
.
platformList
.
map
(
platform
=>
({
...
@@ -300,7 +495,7 @@ const API_URL = "/api/queue/status";
...
@@ -300,7 +495,7 @@ const API_URL = "/api/queue/status";
platformBtn
.
className
=
`trend-switch-btn
${
trendChartMode
===
"platform"
?
"active"
:
""
}
`
;
platformBtn
.
className
=
`trend-switch-btn
${
trendChartMode
===
"platform"
?
"active"
:
""
}
`
;
totalBtn
.
className
=
`trend-switch-btn
${
trendChartMode
===
"total"
?
"active"
:
""
}
`
;
totalBtn
.
className
=
`trend-switch-btn
${
trendChartMode
===
"total"
?
"active"
:
""
}
`
;
title
.
innerText
=
titleText
;
title
.
innerText
=
titleText
;
range
.
innerText
=
`
${
lastTrendData
.
labels
[
lastTrendData
.
labels
.
length
-
1
]}
至
${
lastTrendData
.
labels
[
0
]}
`
;
range
.
innerText
=
`
${
lastTrendData
.
labels
[
0
]}
至
${
lastTrendData
.
labels
[
lastTrendData
.
labels
.
length
-
1
]}
`
;
platformBtn
.
innerText
=
"平台"
;
platformBtn
.
innerText
=
"平台"
;
totalBtn
.
innerText
=
"总量"
;
totalBtn
.
innerText
=
"总量"
;
platformBtn
.
onclick
=
()
=>
setTrendChartMode
(
"platform"
);
platformBtn
.
onclick
=
()
=>
setTrendChartMode
(
"platform"
);
...
@@ -584,6 +779,7 @@ const API_URL = "/api/queue/status";
...
@@ -584,6 +779,7 @@ const API_URL = "/api/queue/status";
hideError
();
hideError
();
statsNote
.
innerText
=
"加载中"
;
statsNote
.
innerText
=
"加载中"
;
trendChartMode
=
"platform"
;
trendChartMode
=
"platform"
;
dailySourceMode
=
"all"
;
setTrendLoading
(
true
);
setTrendLoading
(
true
);
try
{
try
{
...
@@ -597,7 +793,560 @@ const API_URL = "/api/queue/status";
...
@@ -597,7 +793,560 @@ const API_URL = "/api/queue/status";
}
}
}
}
function
formatNumber
(
value
)
{
return
Number
(
value
||
0
).
toLocaleString
(
"zh-CN"
);
}
function
setThirdQuickRange
(
days
,
shouldLoad
=
true
)
{
const
beginInput
=
document
.
getElementById
(
"thirdBeginTime"
);
const
endInput
=
document
.
getElementById
(
"thirdEndTime"
);
const
now
=
new
Date
();
const
start
=
new
Date
(
now
);
start
.
setDate
(
start
.
getDate
()
-
days
+
1
);
start
.
setHours
(
0
,
0
,
0
,
0
);
beginInput
.
value
=
toDateInputValue
(
start
);
endInput
.
value
=
toDateInputValue
(
now
);
[
3
,
7
,
30
].
forEach
(
rangeDays
=>
{
document
.
getElementById
(
`thirdRange
${
rangeDays
}
Btn`
).
classList
.
toggle
(
"active"
,
days
===
rangeDays
);
});
if
(
shouldLoad
&&
currentPanel
===
"third"
)
{
loadThirdPartyStats
();
}
else
if
(
shouldLoad
)
{
loadedPanels
.
third
=
false
;
}
}
function
clearThirdQuickRangeActive
()
{
[
3
,
7
,
30
].
forEach
(
days
=>
{
document
.
getElementById
(
`thirdRange
${
days
}
Btn`
).
classList
.
remove
(
"active"
);
});
}
function
buildThirdStatsUrl
()
{
const
begin
=
document
.
getElementById
(
"thirdBeginTime"
).
value
;
const
end
=
document
.
getElementById
(
"thirdEndTime"
).
value
;
const
channel
=
document
.
getElementById
(
"thirdChannelFilter"
).
value
;
const
platform
=
document
.
getElementById
(
"thirdPlatformFilter"
).
value
;
if
(
!
begin
||
!
end
)
{
throw
new
Error
(
"请选择开始时间和结束时间"
);
}
if
(
begin
>
end
)
{
throw
new
Error
(
"开始时间不能晚于结束时间"
);
}
return
`
${
THIRD_STATS_API_URL
}
?
${
new
URLSearchParams
({
begin
,
end
,
channel
,
platform
}).
toString
()}
`
;
}
function
refreshCustomSelect
(
select
)
{
const
custom
=
select
.
_customSelect
;
if
(
!
custom
)
{
return
;
}
custom
.
menu
.
innerHTML
=
""
;
Array
.
from
(
select
.
options
).
forEach
(
option
=>
{
const
item
=
document
.
createElement
(
"button"
);
item
.
type
=
"button"
;
item
.
className
=
"custom-select-option"
;
item
.
textContent
=
option
.
textContent
;
item
.
dataset
.
value
=
option
.
value
;
item
.
classList
.
toggle
(
"selected"
,
option
.
value
===
select
.
value
);
item
.
setAttribute
(
"aria-selected"
,
option
.
value
===
select
.
value
?
"true"
:
"false"
);
item
.
onclick
=
()
=>
{
if
(
select
.
value
!==
option
.
value
)
{
select
.
value
=
option
.
value
;
select
.
dispatchEvent
(
new
Event
(
"change"
,
{
bubbles
:
true
}));
}
refreshCustomSelect
(
select
);
custom
.
wrapper
.
classList
.
remove
(
"open"
);
custom
.
trigger
.
setAttribute
(
"aria-expanded"
,
"false"
);
custom
.
trigger
.
focus
();
};
custom
.
menu
.
appendChild
(
item
);
});
const
selectedOption
=
select
.
options
[
select
.
selectedIndex
];
custom
.
value
.
textContent
=
selectedOption
?
selectedOption
.
textContent
:
"请选择"
;
}
function
initCustomSelect
(
select
)
{
if
(
select
.
_customSelect
)
{
refreshCustomSelect
(
select
);
return
;
}
const
wrapper
=
document
.
createElement
(
"div"
);
const
trigger
=
document
.
createElement
(
"button"
);
const
value
=
document
.
createElement
(
"span"
);
const
arrow
=
document
.
createElement
(
"span"
);
const
menu
=
document
.
createElement
(
"div"
);
wrapper
.
className
=
"custom-select"
;
trigger
.
type
=
"button"
;
trigger
.
className
=
"custom-select-trigger"
;
trigger
.
setAttribute
(
"aria-haspopup"
,
"listbox"
);
trigger
.
setAttribute
(
"aria-expanded"
,
"false"
);
value
.
className
=
"custom-select-value"
;
arrow
.
className
=
"custom-select-arrow"
;
menu
.
className
=
"custom-select-menu"
;
menu
.
setAttribute
(
"role"
,
"listbox"
);
trigger
.
appendChild
(
value
);
trigger
.
appendChild
(
arrow
);
wrapper
.
appendChild
(
trigger
);
wrapper
.
appendChild
(
menu
);
select
.
insertAdjacentElement
(
"afterend"
,
wrapper
);
select
.
classList
.
add
(
"native-select-hidden"
);
select
.
_customSelect
=
{
wrapper
,
trigger
,
value
,
menu
};
trigger
.
onclick
=
event
=>
{
event
.
stopPropagation
();
document
.
querySelectorAll
(
".custom-select.open"
).
forEach
(
item
=>
{
if
(
item
!==
wrapper
)
{
item
.
classList
.
remove
(
"open"
);
item
.
querySelector
(
".custom-select-trigger"
)
?.
setAttribute
(
"aria-expanded"
,
"false"
);
}
});
const
isOpen
=
wrapper
.
classList
.
toggle
(
"open"
);
trigger
.
setAttribute
(
"aria-expanded"
,
isOpen
?
"true"
:
"false"
);
};
trigger
.
onkeydown
=
event
=>
{
if
(
event
.
key
===
"Escape"
)
{
wrapper
.
classList
.
remove
(
"open"
);
trigger
.
setAttribute
(
"aria-expanded"
,
"false"
);
}
};
refreshCustomSelect
(
select
);
}
function
updateThirdFilterOptions
(
data
)
{
const
channelSelect
=
document
.
getElementById
(
"thirdChannelFilter"
);
const
platformSelect
=
document
.
getElementById
(
"thirdPlatformFilter"
);
const
selectedChannel
=
channelSelect
.
value
;
const
selectedPlatform
=
platformSelect
.
value
;
channelSelect
.
innerHTML
=
'<option value="">全部渠道</option>'
;
(
data
.
channels
||
[]).
forEach
(
item
=>
{
const
option
=
document
.
createElement
(
"option"
);
option
.
value
=
item
.
channel
;
option
.
textContent
=
item
.
remark
||
"-"
;
channelSelect
.
appendChild
(
option
);
});
channelSelect
.
value
=
selectedChannel
;
platformSelect
.
innerHTML
=
'<option value="">全部平台</option>'
;
platforms
.
forEach
(
platform
=>
{
const
option
=
document
.
createElement
(
"option"
);
option
.
value
=
platform
;
option
.
textContent
=
getPlatformDisplayName
(
platform
);
platformSelect
.
appendChild
(
option
);
});
platformSelect
.
value
=
selectedPlatform
;
refreshCustomSelect
(
channelSelect
);
refreshCustomSelect
(
platformSelect
);
}
function
renderThirdMetrics
(
metrics
)
{
document
.
getElementById
(
"thirdTotalMetric"
).
textContent
=
formatNumber
(
metrics
.
third_total
);
document
.
getElementById
(
"thirdDailyAverageMetric"
).
textContent
=
formatNumber
(
metrics
.
daily_average
);
document
.
getElementById
(
"thirdRatioMetric"
).
textContent
=
`
${
Number
(
metrics
.
third_ratio
||
0
).
toFixed
(
2
)}
%`
;
document
.
getElementById
(
"thirdShareMetric"
).
textContent
=
formatNumber
(
metrics
.
share_total
);
document
.
getElementById
(
"thirdEnabledMetric"
).
textContent
=
formatNumber
(
metrics
.
enabled_channel_count
);
}
function
renderThirdRanking
(
items
)
{
const
container
=
document
.
getElementById
(
"thirdRanking"
);
const
maxValue
=
Math
.
max
(...
items
.
map
(
item
=>
Number
(
item
.
task_count
||
0
)),
1
);
container
.
innerHTML
=
""
;
if
(
!
items
.
length
)
{
container
.
innerHTML
=
'<div class="third-empty">暂无渠道用量</div>'
;
return
;
}
const
list
=
document
.
createElement
(
"div"
);
list
.
className
=
"third-ranking-list"
;
items
.
forEach
((
item
,
index
)
=>
{
const
row
=
document
.
createElement
(
"button"
);
const
rank
=
document
.
createElement
(
"span"
);
const
name
=
document
.
createElement
(
"span"
);
const
bar
=
document
.
createElement
(
"span"
);
const
barInner
=
document
.
createElement
(
"span"
);
const
count
=
document
.
createElement
(
"strong"
);
row
.
className
=
"third-ranking-row"
;
rank
.
className
=
"third-ranking-index"
;
name
.
className
=
"third-ranking-name"
;
bar
.
className
=
"third-ranking-bar"
;
barInner
.
className
=
"third-ranking-bar-inner"
;
count
.
className
=
"third-ranking-count"
;
rank
.
textContent
=
index
+
1
;
name
.
textContent
=
item
.
remark
||
"-"
;
barInner
.
style
.
width
=
`
${(
Number
(
item
.
task_count
||
0
)
/
maxValue
)
*
100
}
%`
;
count
.
textContent
=
formatNumber
(
item
.
task_count
);
bar
.
appendChild
(
barInner
);
row
.
appendChild
(
rank
);
row
.
appendChild
(
name
);
row
.
appendChild
(
bar
);
row
.
appendChild
(
count
);
row
.
onclick
=
()
=>
{
document
.
getElementById
(
"thirdChannelFilter"
).
value
=
item
.
channel
;
loadThirdPartyStats
();
};
list
.
appendChild
(
row
);
});
container
.
appendChild
(
list
);
}
function
getThirdDateLabels
()
{
const
begin
=
document
.
getElementById
(
"thirdBeginTime"
).
value
;
const
end
=
document
.
getElementById
(
"thirdEndTime"
).
value
;
const
labels
=
[];
const
current
=
new
Date
(
`
${
begin
}
T00:00:00`
);
const
endDate
=
new
Date
(
`
${
end
}
T00:00:00`
);
while
(
current
<=
endDate
)
{
labels
.
push
(
toDateInputValue
(
current
));
current
.
setDate
(
current
.
getDate
()
+
1
);
}
return
labels
;
}
function
normalizeThirdPt
(
value
)
{
const
text
=
String
(
value
||
""
);
return
text
.
length
===
8
?
`
${
text
.
slice
(
0
,
4
)}
-
${
text
.
slice
(
4
,
6
)}
-
${
text
.
slice
(
6
,
8
)}
`
:
text
.
slice
(
0
,
10
);
}
function
renderThirdTrend
()
{
const
chart
=
document
.
getElementById
(
"thirdTrendChart"
);
chart
.
innerHTML
=
""
;
if
(
!
lastThirdData
)
{
return
;
}
const
selectedChannel
=
document
.
getElementById
(
"thirdChannelFilter"
).
value
;
const
channels
=
selectedChannel
?
[
selectedChannel
]
:
(
lastThirdData
.
channels
||
[]).
map
(
item
=>
item
.
channel
);
const
channelRemarkMap
=
new
Map
(
(
lastThirdData
.
channels
||
[]).
map
(
item
=>
[
item
.
channel
,
item
.
remark
||
"-"
])
);
const
labels
=
getThirdDateLabels
();
const
dailyMap
=
new
Map
();
(
lastThirdData
.
daily
||
[]).
forEach
(
item
=>
{
dailyMap
.
set
(
`
${
item
.
channel
}
__
${
normalizeThirdPt
(
item
.
pt
)}
`
,
Number
(
item
.
count
||
0
)
);
});
const
colors
=
[
"#7138f4"
,
"#16a34a"
,
"#f59e0b"
,
"#dc2626"
,
"#7c3aed"
,
"#0891b2"
,
"#db2777"
,
"#475569"
,
"#0f766e"
,
"#9333ea"
,
"#ea580c"
,
"#4f46e5"
,
"#65a30d"
,
"#be123c"
,
"#0369a1"
,
"#854d0e"
];
const
series
=
thirdTrendMode
===
"total"
?
[{
name
:
"每日总量"
,
color
:
"#7138f4"
,
values
:
labels
.
map
(
label
=>
channels
.
reduce
(
(
sum
,
channel
)
=>
(
sum
+
(
dailyMap
.
get
(
`
${
channel
}
__
${
label
}
`
)
||
0
)
),
0
))
}]
:
channels
.
map
((
channel
,
index
)
=>
({
name
:
channelRemarkMap
.
get
(
channel
)
||
"-"
,
color
:
colors
[
index
%
colors
.
length
],
values
:
labels
.
map
(
label
=>
dailyMap
.
get
(
`
${
channel
}
__
${
label
}
`
)
||
0
)
}));
if
(
!
series
.
length
)
{
chart
.
innerHTML
=
'<div class="third-empty">暂无趋势数据</div>'
;
return
;
}
const
legend
=
document
.
createElement
(
"div"
);
const
scroll
=
document
.
createElement
(
"div"
);
const
width
=
Math
.
max
(
chart
.
clientWidth
-
28
,
labels
.
length
*
90
+
90
);
const
height
=
300
;
const
padding
=
{
top
:
24
,
right
:
20
,
bottom
:
42
,
left
:
58
};
const
plotWidth
=
width
-
padding
.
left
-
padding
.
right
;
const
plotHeight
=
height
-
padding
.
top
-
padding
.
bottom
;
const
maxValue
=
Math
.
max
(...
series
.
flatMap
(
item
=>
item
.
values
),
1
);
const
xScale
=
index
=>
labels
.
length
>
1
?
padding
.
left
+
plotWidth
/
(
labels
.
length
-
1
)
*
index
:
padding
.
left
+
plotWidth
/
2
;
const
yScale
=
value
=>
padding
.
top
+
plotHeight
-
value
/
maxValue
*
plotHeight
;
const
svg
=
createSvgEl
(
"svg"
,
{
viewBox
:
`0 0
${
width
}
${
height
}
`
,
width
,
height
,
role
:
"img"
});
legend
.
className
=
"third-trend-legend"
;
scroll
.
className
=
"third-trend-scroll"
;
series
.
forEach
(
item
=>
{
const
legendItem
=
document
.
createElement
(
"span"
);
legendItem
.
innerHTML
=
`<i style="background:
${
item
.
color
}
"></i>`
;
legendItem
.
appendChild
(
document
.
createTextNode
(
item
.
name
));
legend
.
appendChild
(
legendItem
);
});
for
(
let
index
=
0
;
index
<=
4
;
index
++
)
{
const
value
=
Math
.
round
(
maxValue
/
4
*
index
);
const
y
=
yScale
(
value
);
svg
.
appendChild
(
createSvgEl
(
"line"
,
{
x1
:
padding
.
left
,
y1
:
y
,
x2
:
width
-
padding
.
right
,
y2
:
y
,
stroke
:
index
===
0
?
"#cbd5e1"
:
"#e5e7eb"
,
"stroke-width"
:
"1"
}));
const
text
=
createSvgEl
(
"text"
,
{
x
:
padding
.
left
-
9
,
y
:
y
+
4
,
"text-anchor"
:
"end"
,
class
:
"third-trend-label"
});
text
.
textContent
=
value
;
svg
.
appendChild
(
text
);
}
series
.
forEach
(
item
=>
{
const
points
=
item
.
values
.
map
((
value
,
index
)
=>
({
value
,
x
:
xScale
(
index
),
y
:
yScale
(
value
),
date
:
labels
[
index
]
}));
svg
.
appendChild
(
createSvgEl
(
"polyline"
,
{
points
:
points
.
map
(
point
=>
`
${
point
.
x
}
,
${
point
.
y
}
`
).
join
(
" "
),
fill
:
"none"
,
stroke
:
item
.
color
,
"stroke-width"
:
thirdTrendMode
===
"total"
?
"3"
:
"2"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
}));
points
.
forEach
(
point
=>
{
const
circle
=
createSvgEl
(
"circle"
,
{
cx
:
point
.
x
,
cy
:
point
.
y
,
r
:
thirdTrendMode
===
"total"
?
"4"
:
"3"
,
fill
:
item
.
color
,
stroke
:
"#fff"
,
"stroke-width"
:
"1.5"
});
const
title
=
createSvgEl
(
"title"
);
title
.
textContent
=
`
${
item
.
name
}
${
point
.
date
}
:
${
point
.
value
}
`
;
circle
.
appendChild
(
title
);
svg
.
appendChild
(
circle
);
});
});
labels
.
forEach
((
label
,
index
)
=>
{
const
text
=
createSvgEl
(
"text"
,
{
x
:
xScale
(
index
),
y
:
height
-
13
,
"text-anchor"
:
"middle"
,
class
:
"third-trend-label"
});
text
.
textContent
=
label
.
slice
(
5
);
svg
.
appendChild
(
text
);
});
scroll
.
appendChild
(
svg
);
chart
.
appendChild
(
legend
);
chart
.
appendChild
(
scroll
);
}
function
setThirdTrendMode
(
mode
)
{
thirdTrendMode
=
mode
;
document
.
getElementById
(
"thirdTrendChannelBtn"
).
classList
.
toggle
(
"active"
,
mode
===
"channel"
);
document
.
getElementById
(
"thirdTrendTotalBtn"
).
classList
.
toggle
(
"active"
,
mode
===
"total"
);
renderThirdTrend
();
}
function
renderThirdMatrix
(
data
)
{
const
table
=
document
.
getElementById
(
"thirdMatrixTable"
);
const
selectedChannel
=
document
.
getElementById
(
"thirdChannelFilter"
).
value
;
const
channels
=
selectedChannel
?
[
selectedChannel
]
:
(
data
.
channels
||
[]).
map
(
item
=>
item
.
channel
);
const
platforms
=
[...(
data
.
platforms
||
[])].
sort
((
left
,
right
)
=>
{
const
leftOrder
=
PLATFORM_DISPLAY_ORDER
.
indexOf
(
getPlatformDisplayName
(
left
)
);
const
rightOrder
=
PLATFORM_DISPLAY_ORDER
.
indexOf
(
getPlatformDisplayName
(
right
)
);
return
(
(
leftOrder
===
-
1
?
PLATFORM_DISPLAY_ORDER
.
length
:
leftOrder
)
-
(
rightOrder
===
-
1
?
PLATFORM_DISPLAY_ORDER
.
length
:
rightOrder
)
);
});
const
channelRemarkMap
=
new
Map
(
(
data
.
channels
||
[]).
map
(
item
=>
[
item
.
channel
,
item
.
remark
||
"-"
])
);
const
valueMap
=
new
Map
();
(
data
.
matrix
||
[]).
forEach
(
item
=>
{
valueMap
.
set
(
`
${
item
.
channel
}
__
${
item
.
platform
}
`
,
Number
(
item
.
count
||
0
));
});
const
maxValue
=
Math
.
max
(...
valueMap
.
values
(),
1
);
table
.
innerHTML
=
""
;
const
thead
=
document
.
createElement
(
"thead"
);
const
headRow
=
document
.
createElement
(
"tr"
);
const
channelHead
=
document
.
createElement
(
"th"
);
channelHead
.
textContent
=
"渠道"
;
headRow
.
appendChild
(
channelHead
);
platforms
.
forEach
(
platform
=>
{
const
th
=
document
.
createElement
(
"th"
);
th
.
textContent
=
getPlatformDisplayName
(
platform
);
headRow
.
appendChild
(
th
);
});
const
totalHead
=
document
.
createElement
(
"th"
);
totalHead
.
textContent
=
"合计"
;
headRow
.
appendChild
(
totalHead
);
thead
.
appendChild
(
headRow
);
table
.
appendChild
(
thead
);
const
tbody
=
document
.
createElement
(
"tbody"
);
channels
.
forEach
(
channel
=>
{
const
row
=
document
.
createElement
(
"tr"
);
const
channelCell
=
document
.
createElement
(
"td"
);
let
rowTotal
=
0
;
channelCell
.
textContent
=
channelRemarkMap
.
get
(
channel
)
||
"-"
;
row
.
appendChild
(
channelCell
);
platforms
.
forEach
(
platform
=>
{
const
count
=
valueMap
.
get
(
`
${
channel
}
__
${
platform
}
`
)
||
0
;
const
cell
=
document
.
createElement
(
"td"
);
rowTotal
+=
count
;
cell
.
textContent
=
formatNumber
(
count
);
cell
.
style
.
background
=
`rgba(113, 56, 244,
${
0.04
+
count
/
maxValue
*
0.52
}
)`
;
cell
.
style
.
color
=
count
/
maxValue
>
0.58
?
"#fff"
:
"#1e293b"
;
row
.
appendChild
(
cell
);
});
const
totalCell
=
document
.
createElement
(
"td"
);
totalCell
.
textContent
=
formatNumber
(
rowTotal
);
totalCell
.
className
=
"third-matrix-total"
;
row
.
appendChild
(
totalCell
);
tbody
.
appendChild
(
row
);
});
table
.
appendChild
(
tbody
);
}
function
renderThirdDetails
(
items
)
{
const
tbody
=
document
.
getElementById
(
"thirdDetailBody"
);
tbody
.
innerHTML
=
""
;
items
.
forEach
(
item
=>
{
const
row
=
document
.
createElement
(
"tr"
);
[
item
.
remark
||
"-"
,
formatNumber
(
item
.
task_count
),
`
${
Number
(
item
.
ratio
||
0
).
toFixed
(
2
)}
%`
,
formatNumber
(
item
.
share_count
),
formatNumber
(
item
.
platform_count
),
normalizeThirdPt
(
item
.
last_date
)
||
"-"
,
formatNumber
(
item
.
daily_limit
),
formatNumber
(
item
.
total_limit
)
].
forEach
(
value
=>
{
const
cell
=
document
.
createElement
(
"td"
);
cell
.
textContent
=
value
;
row
.
appendChild
(
cell
);
});
tbody
.
appendChild
(
row
);
});
}
function
renderThirdPartyStats
(
responseData
)
{
const
data
=
responseData
&&
responseData
.
data
?
responseData
.
data
:
{};
lastThirdData
=
data
;
updateThirdFilterOptions
(
data
);
renderThirdMetrics
(
data
.
metrics
||
{});
renderThirdRanking
(
data
.
channel_ranking
||
[]);
renderThirdTrend
();
renderThirdMatrix
(
data
);
renderThirdDetails
(
data
.
channel_details
||
[]);
const
labels
=
getThirdDateLabels
();
document
.
getElementById
(
"thirdStatsNote"
).
textContent
=
(
`当前启用
${
Number
((
data
.
metrics
||
{}).
enabled_channel_count
||
0
)}
个渠道,`
+
`
${
labels
[
0
]
||
"-"
}
至
${
labels
[
labels
.
length
-
1
]
||
"-"
}
`
);
document
.
getElementById
(
"lastTime"
).
innerText
=
"最后刷新:"
+
new
Date
().
toLocaleString
();
}
async
function
loadThirdPartyStats
()
{
const
note
=
document
.
getElementById
(
"thirdStatsNote"
);
hideError
();
note
.
textContent
=
"加载中"
;
setThirdLoading
(
true
);
try
{
renderThirdPartyStats
(
await
fetchJson
(
buildThirdStatsUrl
()));
loadedPanels
.
third
=
true
;
}
catch
(
error
)
{
note
.
textContent
=
"加载失败"
;
showError
(
"三方用量数据加载失败:"
+
error
.
message
);
}
finally
{
setThirdLoading
(
false
);
}
}
initCustomSelect
(
document
.
getElementById
(
"thirdChannelFilter"
));
initCustomSelect
(
document
.
getElementById
(
"thirdPlatformFilter"
));
document
.
addEventListener
(
"click"
,
event
=>
{
if
(
!
event
.
target
.
closest
(
".custom-select"
))
{
document
.
querySelectorAll
(
".custom-select.open"
).
forEach
(
item
=>
{
item
.
classList
.
remove
(
"open"
);
item
.
querySelector
(
".custom-select-trigger"
)
?.
setAttribute
(
"aria-expanded"
,
"false"
);
});
}
});
document
.
getElementById
(
"beginTime"
).
addEventListener
(
"change"
,
handleDateRangeChange
);
document
.
getElementById
(
"beginTime"
).
addEventListener
(
"change"
,
handleDateRangeChange
);
document
.
getElementById
(
"endTime"
).
addEventListener
(
"change"
,
handleDateRangeChange
);
document
.
getElementById
(
"endTime"
).
addEventListener
(
"change"
,
handleDateRangeChange
);
document
.
getElementById
(
"thirdBeginTime"
).
addEventListener
(
"change"
,
()
=>
{
clearThirdQuickRangeActive
();
loadThirdPartyStats
();
});
document
.
getElementById
(
"thirdEndTime"
).
addEventListener
(
"change"
,
()
=>
{
clearThirdQuickRangeActive
();
loadThirdPartyStats
();
});
document
.
getElementById
(
"thirdChannelFilter"
).
addEventListener
(
"change"
,
loadThirdPartyStats
);
document
.
getElementById
(
"thirdPlatformFilter"
).
addEventListener
(
"change"
,
loadThirdPartyStats
);
setQuickRange
(
3
,
false
);
setQuickRange
(
3
,
false
);
setThirdQuickRange
(
3
,
false
);
loadQueueStatus
();
loadQueueStatus
();
aidso_geo/core/templates/dashboard_login.html
0 → 100644
View file @
27ac512a
<!DOCTYPE html>
<html
lang=
"zh-CN"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
AIDSO 数据监控 - 登录
</title>
<link
rel=
"stylesheet"
href=
"{{ url_for('static', filename='dashboard/dashboard_login.css') }}"
>
</head>
<body>
<main
class=
"login-page"
>
<section
class=
"login-visual"
>
<div
class=
"visual-content"
>
<div
class=
"visual-badge"
>
AIDSO DATA
</div>
<h1>
数据监控中心
</h1>
<p>
统一查看队列状态、历史任务趋势与三方渠道用量。
</p>
<div
class=
"visual-grid"
aria-hidden=
"true"
>
<span></span><span></span><span></span>
<span></span><span></span><span></span>
</div>
</div>
</section>
<section
class=
"login-panel"
>
<div
class=
"login-card"
>
<img
class=
"login-logo"
src=
"{{ url_for('static', filename='dashboard/aidso_logo.png') }}"
alt=
"AIDSO 爱搜"
>
<div
class=
"login-heading"
>
<h2>
登录数据监控
</h2>
<p>
请输入账号和密码进入系统
</p>
</div>
<form
method=
"post"
action=
"{{ url_for('dashboard.dashboard_login') }}"
>
<label
class=
"login-field"
>
<span>
账号
</span>
<span
class=
"input-shell"
>
<i
class=
"input-icon user-icon"
aria-hidden=
"true"
></i>
<input
type=
"text"
name=
"username"
autocomplete=
"username"
placeholder=
"请输入账号"
required
autofocus
>
</span>
</label>
<label
class=
"login-field"
>
<span>
密码
</span>
<span
class=
"input-shell"
>
<i
class=
"input-icon lock-icon"
aria-hidden=
"true"
></i>
<input
type=
"password"
name=
"password"
autocomplete=
"current-password"
placeholder=
"请输入密码"
required
>
</span>
</label>
{% if error_message %}
<div
class=
"login-error"
role=
"alert"
>
{{ error_message }}
</div>
{% endif %}
<button
class=
"login-button"
type=
"submit"
>
登录
</button>
</form>
<p
class=
"login-footer"
>
AIDSO 数据监控
</p>
</div>
</section>
</main>
</body>
</html>
aidso_geo/core/templates/queue_monitor.html
View file @
27ac512a
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
<html
lang=
"zh-CN"
>
<html
lang=
"zh-CN"
>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<title>
GEO 队列监控面板
</title>
<title>
AIDSO 数据监控
</title>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<link
rel=
"stylesheet"
href=
"{{ url_for('static', filename='dashboard/queue_monitor.css') }}"
>
<link
rel=
"stylesheet"
href=
"{{ url_for('static', filename='dashboard/queue_monitor.css') }}"
>
...
@@ -11,20 +11,36 @@
...
@@ -11,20 +11,36 @@
<body>
<body>
<div
class=
"layout"
>
<div
class=
"layout"
>
<aside
class=
"sidebar"
>
<aside
class=
"sidebar"
>
<button
class=
"nav-btn active"
id=
"queueNavBtn"
onclick=
"showPanel('queue')"
>
积压查看
</button>
<div
class=
"brand"
>
<button
class=
"nav-btn"
id=
"trendNavBtn"
onclick=
"showPanel('trend')"
>
历史任务趋势
</button>
<img
class=
"brand-logo"
src=
"{{ url_for('static', filename='dashboard/aidso_logo.png') }}"
alt=
"AIDSO"
>
</div>
<div
class=
"nav-label"
>
数据看板
</div>
<nav
class=
"nav-list"
>
<button
class=
"nav-btn active"
id=
"queueNavBtn"
onclick=
"showPanel('queue')"
>
积压查看
</button>
<button
class=
"nav-btn"
id=
"trendNavBtn"
onclick=
"showPanel('trend')"
>
历史任务趋势
</button>
<button
class=
"nav-btn"
id=
"thirdNavBtn"
onclick=
"showPanel('third')"
>
三方用量分析
</button>
</nav>
<div
class=
"sidebar-foot"
>
<span
class=
"health-dot"
></span>
<span>
数据服务运行中
</span>
</div>
</aside>
</aside>
<main
class=
"main"
>
<main
class=
"main"
>
<div
class=
"header"
>
<div
class=
"header"
>
<div>
<div>
<div
class=
"title"
id=
"pageTitle"
>
GEO 队列监控面板
</div>
<div
class=
"title"
id=
"pageTitle"
>
队列积压监控
</div>
<div
class=
"desc"
id=
"pageDesc"
>
查看各平台 stream_batch / batch 队列积压情况
</div>
<div
class=
"desc"
id=
"pageDesc"
>
查看各平台 stream_batch / batch 队列积压情况
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
>
<span
class=
"last-time"
id=
"lastTime"
>
未刷新
</span>
<span
class=
"last-time"
id=
"lastTime"
>
未刷新
</span>
<button
class=
"btn"
onclick=
"refreshCurrentPanel()"
>
刷新
</button>
<button
class=
"btn"
onclick=
"refreshCurrentPanel()"
>
刷新
</button>
<a
class=
"logout-link"
href=
"{{ url_for('dashboard.dashboard_logout') }}"
>
退出
</a>
</div>
</div>
</div>
</div>
...
@@ -48,11 +64,11 @@
...
@@ -48,11 +64,11 @@
</div>
</div>
</div>
</div>
<div
class=
"section-head"
>
<div
class=
"section-title"
>
队列积压详情
</div>
<div
class=
"section-note"
>
Redis 队列长度
</div>
</div>
<div
class=
"table-wrap"
>
<div
class=
"table-wrap"
>
<div
class=
"section-head panel-card-head"
>
<div
class=
"section-title"
>
队列积压详情
</div>
<div
class=
"section-note"
>
Redis 队列长度
</div>
</div>
<table>
<table>
<thead>
<thead>
<tr>
<tr>
...
@@ -69,11 +85,11 @@
...
@@ -69,11 +85,11 @@
</section>
</section>
<section
class=
"panel"
id=
"trendPanel"
>
<section
class=
"panel"
id=
"trendPanel"
>
<div
class=
"section-head"
>
<div
class=
"section-title"
>
历史任务趋势
</div>
<div
class=
"section-note"
id=
"statsNote"
>
等待加载
</div>
</div>
<div
class=
"trend-panel"
>
<div
class=
"trend-panel"
>
<div
class=
"section-head panel-card-head"
>
<div
class=
"section-title"
>
历史任务概览
</div>
<div
class=
"section-note"
id=
"statsNote"
>
等待加载
</div>
</div>
<div
class=
"filter-row"
>
<div
class=
"filter-row"
>
<div
class=
"quick-range"
>
<div
class=
"quick-range"
>
<button
class=
"quick-btn active"
id=
"range3Btn"
onclick=
"setQuickRange(3)"
>
近3天
</button>
<button
class=
"quick-btn active"
id=
"range3Btn"
onclick=
"setQuickRange(3)"
>
近3天
</button>
...
@@ -100,6 +116,111 @@
...
@@ -100,6 +116,111 @@
</div>
</div>
</section>
</section>
<section
class=
"panel"
id=
"thirdPanel"
>
<div
class=
"third-panel"
>
<div
class=
"section-head panel-card-head"
>
<div
class=
"section-title"
>
三方用量概览
</div>
<div
class=
"section-note"
id=
"thirdStatsNote"
>
等待加载
</div>
</div>
<div
class=
"filter-row"
>
<div
class=
"quick-range"
>
<button
class=
"quick-btn active"
id=
"thirdRange3Btn"
onclick=
"setThirdQuickRange(3)"
>
近3天
</button>
<button
class=
"quick-btn"
id=
"thirdRange7Btn"
onclick=
"setThirdQuickRange(7)"
>
近7天
</button>
<button
class=
"quick-btn"
id=
"thirdRange30Btn"
onclick=
"setThirdQuickRange(30)"
>
近30天
</button>
</div>
<div
class=
"field"
>
<label
for=
"thirdBeginTime"
>
开始时间
</label>
<input
id=
"thirdBeginTime"
type=
"date"
>
</div>
<div
class=
"field"
>
<label
for=
"thirdEndTime"
>
结束时间
</label>
<input
id=
"thirdEndTime"
type=
"date"
>
</div>
<div
class=
"field"
>
<label
for=
"thirdChannelFilter"
>
渠道
</label>
<select
id=
"thirdChannelFilter"
>
<option
value=
""
>
全部渠道
</option>
</select>
</div>
<div
class=
"field"
>
<label
for=
"thirdPlatformFilter"
>
平台
</label>
<select
id=
"thirdPlatformFilter"
>
<option
value=
""
>
全部平台
</option>
</select>
</div>
</div>
<div
class=
"third-metrics"
>
<div
class=
"third-metric-card"
><span>
三方任务总量
</span><strong
id=
"thirdTotalMetric"
>
0
</strong></div>
<div
class=
"third-metric-card"
><span>
日均任务量
</span><strong
id=
"thirdDailyAverageMetric"
>
0
</strong></div>
<div
class=
"third-metric-card"
><span>
三方任务占比
</span><strong
id=
"thirdRatioMetric"
>
0%
</strong></div>
<div
class=
"third-metric-card"
><span>
三方分享任务
</span><strong
id=
"thirdShareMetric"
>
0
</strong></div>
<div
class=
"third-metric-card"
><span>
当前启用渠道
</span><strong
id=
"thirdEnabledMetric"
>
0
</strong></div>
</div>
<div
class=
"third-dashboard-grid"
>
<section
class=
"third-section-card"
>
<div
class=
"third-card-head"
>
<strong>
渠道用量排行
</strong>
<span>
按任务量降序
</span>
</div>
<div
id=
"thirdRanking"
></div>
</section>
<section
class=
"third-section-card"
>
<div
class=
"third-card-head"
>
<strong>
三方每日用量趋势
</strong>
<div
class=
"third-trend-switch"
>
<button
class=
"active"
id=
"thirdTrendChannelBtn"
onclick=
"setThirdTrendMode('channel')"
>
按渠道
</button>
<button
id=
"thirdTrendTotalBtn"
onclick=
"setThirdTrendMode('total')"
>
每日总量
</button>
</div>
</div>
<div
class=
"third-trend-chart"
id=
"thirdTrendChart"
></div>
</section>
</div>
<section
class=
"third-section-card"
>
<div
class=
"third-card-head"
>
<strong>
渠道 × 平台用量矩阵
</strong>
<span>
颜色越深代表任务量越高
</span>
</div>
<div
class=
"third-table-scroll"
>
<table
class=
"third-matrix-table"
id=
"thirdMatrixTable"
></table>
</div>
</section>
<section
class=
"third-section-card"
>
<div
class=
"third-card-head"
>
<strong>
渠道详细数据
</strong>
<span>
仅展示当前启用渠道
</span>
</div>
<div
class=
"third-table-scroll"
>
<table
class=
"third-detail-table"
>
<thead>
<tr>
<th>
渠道
</th>
<th>
任务量
</th>
<th>
三方用量占比
</th>
<th>
分享任务
</th>
<th>
平台数
</th>
<th>
最近使用
</th>
<th>
日额度
</th>
<th>
总额度
</th>
</tr>
</thead>
<tbody
id=
"thirdDetailBody"
></tbody>
</table>
</div>
</section>
<div
class=
"trend-loading-mask"
id=
"thirdLoadingMask"
>
<div
class=
"loading-box"
>
<span
class=
"loading-spinner"
></span>
<span>
加载中
</span>
</div>
</div>
</div>
</section>
<div
class=
"error"
id=
"errorBox"
></div>
<div
class=
"error"
id=
"errorBox"
></div>
</main>
</main>
</div>
</div>
...
...
aidso_geo/models/douyinai_data_process.py
View file @
27ac512a
import
json
import
json
from
aidso_geo.models
import
spider_save_tos
from
aidso_geo.models
import
spider_save_tos
from
aidso_geo.utils
import
robot_utils
from
aidso_geo.utils
import
robot_utils
,
bh_utils
from
aidso_geo.utils.ai_interface
import
get_parse_sse_result
from
aidso_geo.utils.ai_interface
import
get_parse_sse_result
from
aidso_geo.utils.tos_utils
import
get_string_from_tos
from
aidso_geo.utils.tos_utils
import
get_string_from_tos
...
@@ -31,6 +31,7 @@ def douyin_ai_process_original_data(data):
...
@@ -31,6 +31,7 @@ def douyin_ai_process_original_data(data):
# 提取并解析JSON数据
# 提取并解析JSON数据
data_str
=
item
.
split
(
"data:"
)[
1
]
data_str
=
item
.
split
(
"data:"
)[
1
]
json_data
=
json
.
loads
(
data_str
)
json_data
=
json
.
loads
(
data_str
)
except
(
IndexError
,
json
.
JSONDecodeError
):
except
(
IndexError
,
json
.
JSONDecodeError
):
continue
continue
...
@@ -117,6 +118,30 @@ def douyin_ai_process_original_data(data):
...
@@ -117,6 +118,30 @@ def douyin_ai_process_original_data(data):
return
(
file_path
,
search_keyword
,
url_list
,
think_content
,
response_content
,
suggestions
)
return
(
file_path
,
search_keyword
,
url_list
,
think_content
,
response_content
,
suggestions
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
file_path
=
"geo/8004e38c-37da-4e82-b08f-453ccdf0d661/DYAI/original.text"
data_list
=
bh_utils
.
query_data
(
douyin_ai_process_original_data
(
file_path
)
f
"select * from geo_commit_task where taskId = '1cfb92e0-b0ae-40e5-b198-342d079dea0a' and platform = 'DYAI'"
)
def
handle_item
(
i
):
if
i
.
get
(
'comWordsMap'
):
i
[
'comWordsMap'
]
=
json
.
loads
(
i
.
get
(
'comWordsMap'
))
if
i
.
get
(
'brandWords'
):
i
[
'brandWords'
]
=
json
.
loads
(
i
.
get
(
'brandWords'
))
if
i
.
get
(
'comWords'
):
i
[
'comWords'
]
=
json
.
loads
(
i
.
get
(
'comWords'
))
if
i
.
get
(
'keywords'
):
i
[
'keywords'
]
=
json
.
loads
(
i
.
get
(
'keywords'
))
if
i
.
get
(
'productWordsMap'
):
i
[
'productWordsMap'
]
=
json
.
loads
(
i
.
get
(
'productWordsMap'
))
return
douyin_ai_process_original_data
(
i
)
if
data_list
:
for
i
in
data_list
:
try
:
handle_item
(
i
)
except
Exception
as
e
:
...
aidso_geo/models/qianwen_android_data_process.py
View file @
27ac512a
...
@@ -8,8 +8,50 @@ from aidso_geo.utils.ai_interface import get_parse_sse_result
...
@@ -8,8 +8,50 @@ from aidso_geo.utils.ai_interface import get_parse_sse_result
from
aidso_geo.utils.tos_utils
import
get_string_from_tos
from
aidso_geo.utils.tos_utils
import
get_string_from_tos
def
qianwen_android_process_original_data
(
task_data
):
def
process_source
(
source
):
source_json
=
[]
if
isinstance
(
source
,
str
):
try
:
source_json
=
json
.
loads
(
source
)
except
json
.
JSONDecodeError
:
source_json
=
[]
if
not
isinstance
(
source_json
,
list
):
source_json
=
[]
url_content_list
=
[]
if
source_json
:
for
s
in
source_json
:
normalized_url
=
s
.
get
(
"normalized_url"
)
FIXED_URL
=
"https://www.amap.com"
url_process
=
{
"url"
:
(
FIXED_URL
if
not
normalized_url
and
s
.
get
(
"HostName"
)
==
"高德地图"
else
normalized_url
),
"title"
:
s
.
get
(
"title"
),
"summary"
:
s
.
get
(
"page_content"
),
"publish_time"
:
s
.
get
(
"publish_time"
),
"name"
:
s
.
get
(
"HostName"
),
"icon"
:
""
,
"source_seq"
:
''
,
}
url_content_list
.
append
(
url_process
)
result
=
[{
"source_seq"
:
""
,
"url"
:
[
{
"type"
:
"source"
,
"content"
:
{
"list"
:
url_content_list
},
}
]
}]
return
result
def
qianwen_android_process_original_data
(
task_data
):
file_path
=
f
'geo/{task_data["taskId"]}/{task_data["platform"]}/original.text'
file_path
=
f
'geo/{task_data["taskId"]}/{task_data["platform"]}/original.text'
url_list
=
[]
url_list
=
[]
url_list_batch
=
[]
url_list_batch
=
[]
...
@@ -30,14 +72,14 @@ def qianwen_android_process_original_data(task_data):
...
@@ -30,14 +72,14 @@ def qianwen_android_process_original_data(task_data):
try
:
try
:
sse_json_data
=
json
.
loads
(
original_content
)
sse_json_data
=
json
.
loads
(
original_content
)
# sse_json_data = json.loads(json_original.get('sse'))
# sse_json_data = json.loads(json_original.get('sse'))
response_content
=
sse_json_data
.
get
(
'reply_text'
,
''
)
response_content
=
sse_json_data
.
get
(
'reply_text'
,
''
)
think_content
=
sse_json_data
.
get
(
'deepthink_text'
,
''
)
think_content
=
sse_json_data
.
get
(
'deepthink_text'
,
''
)
paa_answer
=
sse_json_data
.
get
(
'paa_answer'
,
''
)
paa_answer
=
sse_json_data
.
get
(
'paa_answer'
,
''
)
bar_sources
=
sse_json_data
.
get
(
'bar_sources'
)
bar_sources
=
sse_json_data
.
get
(
'bar_sources'
)
amap_pois
=
sse_json_data
.
get
(
'amap_pois'
)
amap_pois
=
sse_json_data
.
get
(
'amap_pois'
)
gaode_list
=
[]
gaode_list
=
[]
if
amap_pois
:
if
amap_pois
:
for
key
,
value
in
amap_pois
.
items
():
for
key
,
value
in
amap_pois
.
items
():
for
v
in
value
:
for
v
in
value
:
modelInput
=
v
.
get
(
'future_data'
)
.
get
(
'modelInput'
)
modelInput
=
v
.
get
(
'future_data'
)
.
get
(
'modelInput'
)
biz_ext
=
v
.
get
(
'biz_ext'
)
biz_ext
=
v
.
get
(
'biz_ext'
)
...
@@ -71,7 +113,7 @@ def qianwen_android_process_original_data(task_data):
...
@@ -71,7 +113,7 @@ def qianwen_android_process_original_data(task_data):
response_content
+=
'render_ecom_card_widget_gaode_end:
\n
'
response_content
+=
'render_ecom_card_widget_gaode_end:
\n
'
source_content
=
{}
source_content
=
{}
if
bar_sources
:
if
bar_sources
:
source_content
=
next
(
source_content
=
next
(
(
item
.
get
(
'content'
)
for
item
in
bar_sources
if
item
.
get
(
'type'
)
==
'source'
),
(
item
.
get
(
'content'
)
for
item
in
bar_sources
if
item
.
get
(
'type'
)
==
'source'
),
{}
{}
)
)
...
@@ -89,43 +131,43 @@ def qianwen_android_process_original_data(task_data):
...
@@ -89,43 +131,43 @@ def qianwen_android_process_original_data(task_data):
if
video_note_list
:
if
video_note_list
:
for
i
in
video_note_list
:
for
i
in
video_note_list
:
rich_media
=
{
rich_media
=
{
'zhidaye_id'
:
i
.
get
(
'zhidaye_id'
),
'zhidaye_id'
:
i
.
get
(
'zhidaye_id'
),
'title'
:
i
.
get
(
'title'
),
'title'
:
i
.
get
(
'title'
),
'cover'
:
i
.
get
(
'cover'
),
'cover'
:
i
.
get
(
'cover'
),
'url'
:
i
.
get
(
'url'
),
'url'
:
i
.
get
(
'url'
),
'author'
:
i
.
get
(
'author'
),
'author'
:
i
.
get
(
'author'
),
'publishTime'
:
i
.
get
(
'publishTime'
),
'publishTime'
:
i
.
get
(
'publishTime'
),
'duration'
:
i
.
get
(
'duration'
),
'duration'
:
i
.
get
(
'duration'
),
}
}
video_list
.
append
(
rich_media
)
video_list
.
append
(
rich_media
)
rich_media_block
.
append
({
rich_media_block
.
append
({
"url"
:
video_list
,
"url"
:
video_list
,
"source_seq"
:
''
"source_seq"
:
''
})
})
source_count_list
=
[]
source_count_list
=
[]
if
source_list
:
if
source_list
:
for
s
in
source_list
:
for
s
in
source_list
:
source_url
=
{
source_url
=
{
'url'
:
s
.
get
(
'url'
),
'url'
:
s
.
get
(
'url'
),
'title'
:
s
.
get
(
'title'
),
'title'
:
s
.
get
(
'title'
),
'summary'
:
s
.
get
(
'summary'
),
'summary'
:
s
.
get
(
'summary'
),
'publish_time'
:
s
.
get
(
'publish_time'
),
'publish_time'
:
s
.
get
(
'publish_time'
),
'name'
:
s
.
get
(
'name'
),
'name'
:
s
.
get
(
'name'
),
'icon'
:
s
.
get
(
'icon'
),
'icon'
:
s
.
get
(
'icon'
),
'source_seq'
:
s
.
get
(
'source_seq'
,
''
),
'source_seq'
:
s
.
get
(
'source_seq'
,
''
),
}
}
source_count_list
.
append
(
source_url
)
source_count_list
.
append
(
source_url
)
url_list
.
append
({
url_list
.
append
({
"url"
:[
"url"
:
[
{
{
"type"
:
"source"
,
"type"
:
"source"
,
"content"
:{
"content"
:
{
"list"
:
source_count_list
"list"
:
source_count_list
},
},
}
}
],
],
"source_seq"
:
''
"source_seq"
:
''
})
})
if
paa_answer
:
if
paa_answer
:
...
@@ -187,11 +229,13 @@ def qianwen_android_process_original_data(task_data):
...
@@ -187,11 +229,13 @@ def qianwen_android_process_original_data(task_data):
if
mime_type
==
'bar/progress'
and
meta_data_type
==
'bar_update'
:
if
mime_type
==
'bar/progress'
and
meta_data_type
==
'bar_update'
:
if
messages
[
0
]
.
get
(
'meta_data'
):
if
messages
[
0
]
.
get
(
'meta_data'
):
if
messages
[
0
]
.
get
(
'meta_data'
)
.
get
(
'elements'
):
if
messages
[
0
]
.
get
(
'meta_data'
)
.
get
(
'elements'
):
response_content
+=
messages
[
0
]
.
get
(
'meta_data'
)
.
get
(
'elements'
)[
0
]
.
get
(
'content'
)
response_content
+=
messages
[
0
]
.
get
(
'meta_data'
)
.
get
(
'elements'
)[
0
]
.
get
(
'content'
)
# 引用来源-无下标
# 引用来源-无下标
if
mime_type
==
'multi_load/iframe'
:
if
mime_type
==
'multi_load/iframe'
:
for
i
in
multi_load
:
for
i
in
multi_load
:
if
i
.
get
(
'type'
)
==
'ref_source_inline'
and
i
.
get
(
'content'
)
.
get
(
'status'
)
==
'complete'
:
if
i
.
get
(
'type'
)
==
'ref_source_inline'
and
i
.
get
(
'content'
)
.
get
(
'status'
)
==
'complete'
:
multi_load_content
=
i
.
get
(
'content'
)
multi_load_content
=
i
.
get
(
'content'
)
multi_load_source_seq
=
i
.
get
(
'source_seq'
)
multi_load_source_seq
=
i
.
get
(
'source_seq'
)
multi_load_content_query_list
=
multi_load_content
.
get
(
'query_list'
)
multi_load_content_query_list
=
multi_load_content
.
get
(
'query_list'
)
...
@@ -215,18 +259,19 @@ def qianwen_android_process_original_data(task_data):
...
@@ -215,18 +259,19 @@ def qianwen_android_process_original_data(task_data):
"source_seq"
:
''
,
"source_seq"
:
''
,
})
})
taobao_list
=
[]
taobao_list
=
[]
if
mime_type
==
'multi_load/iframe'
and
(
multi_load_type
==
'taoassistant_fold_product_feeds'
or
multi_load_type
==
'taoassistant_single_product'
or
multi_load_type
==
'taoassistant_single_product_v2'
)
and
status
==
'complete'
:
if
mime_type
==
'multi_load/iframe'
and
(
multi_load_type
==
'taoassistant_fold_product_feeds'
or
multi_load_type
==
'taoassistant_single_product'
or
multi_load_type
==
'taoassistant_single_product_v2'
)
and
status
==
'complete'
:
for
mu
in
multi_load
:
for
mu
in
multi_load
:
if
mu
.
get
(
'type'
)
==
'taoassistant_fold_product_feeds'
:
if
mu
.
get
(
'type'
)
==
'taoassistant_fold_product_feeds'
:
source_seq
=
mu
.
get
(
'source_seq'
)
source_seq
=
mu
.
get
(
'source_seq'
)
jump_url
=
next
(
jump_url
=
next
(
(
(
item
.
get
(
'params'
,
{})
.
get
(
'popupLink'
,
''
)
item
.
get
(
'params'
,
{})
.
get
(
'popupLink'
,
''
)
for
item
in
for
item
in
mu
.
get
(
'content'
,
{})
.
get
(
'cardData'
,
{})
.
get
(
'clientActions'
,
[])
mu
.
get
(
'content'
,
{})
.
get
(
'cardData'
,
{})
.
get
(
'clientActions'
,
[])
if
item
.
get
(
'type'
)
==
'popupLink'
if
item
.
get
(
'type'
)
==
'popupLink'
),
),
''
''
...
@@ -234,23 +279,26 @@ def qianwen_android_process_original_data(task_data):
...
@@ -234,23 +279,26 @@ def qianwen_android_process_original_data(task_data):
query
=
next
(
query
=
next
(
(
(
item
.
get
(
'params'
,
{})
.
get
(
'params'
,
{})
.
get
(
'queryList'
,
[])
item
.
get
(
'params'
,
{})
.
get
(
'params'
,
{})
.
get
(
'queryList'
,
[])
for
item
in
for
item
in
mu
.
get
(
'content'
,
{})
.
get
(
'cardData'
,
{})
.
get
(
'clientActions'
,
[])
mu
.
get
(
'content'
,
{})
.
get
(
'cardData'
,
{})
.
get
(
'clientActions'
,
[])
if
item
.
get
(
'type'
)
==
'loadmore'
if
item
.
get
(
'type'
)
==
'loadmore'
),
),
''
''
)
)
if
isinstance
(
query
,
str
):
if
isinstance
(
query
,
str
):
if
query
:
if
query
:
query
=
json
.
loads
(
query
)
query
=
json
.
loads
(
query
)
if
isinstance
(
query
,
list
):
if
isinstance
(
query
,
list
):
search_keyword
.
extend
(
query
)
search_keyword
.
extend
(
query
)
if
isinstance
(
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'items'
),
list
)
and
not
taobao_list
:
if
isinstance
(
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'items'
),
list
)
and
not
taobao_list
:
cateTitle
=
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'cateTitle'
)
cateTitle
=
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'cateTitle'
)
for
pro
in
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'items'
):
for
pro
in
mu
.
get
(
'content'
)
.
get
(
'cardData'
)
.
get
(
'data'
)
.
get
(
'items'
):
price
=
(
pro
.
get
(
'priceShowWithIcon'
)
or
{})
.
get
(
'price'
)
or
pro
.
get
(
'itemPrice'
)
or
''
price
=
(
pro
.
get
(
'priceShowWithIcon'
)
or
{})
.
get
(
'price'
)
or
pro
.
get
(
'itemPrice'
)
or
''
taobao_list
.
append
({
taobao_list
.
append
({
"title"
:
cateTitle
or
pro
.
get
(
'title'
,
''
),
"title"
:
cateTitle
or
pro
.
get
(
'title'
,
''
),
"shop_name"
:
next
(
"shop_name"
:
next
(
...
@@ -268,17 +316,19 @@ def qianwen_android_process_original_data(task_data):
...
@@ -268,17 +316,19 @@ def qianwen_android_process_original_data(task_data):
"source_seq"
:
source_seq
,
"source_seq"
:
source_seq
,
"auctionURL"
:
pro
.
get
(
'auctionURL'
),
"auctionURL"
:
pro
.
get
(
'auctionURL'
),
"item_id"
:
pro
.
get
(
'item_id'
),
"item_id"
:
pro
.
get
(
'item_id'
),
'card_type'
:
'fold_product'
'card_type'
:
'fold_product'
})
})
if
mu
.
get
(
'type'
)
in
(
'taoassistant_single_product'
,
'taoassistant_single_product_v2'
):
if
mu
.
get
(
'type'
)
in
(
mu_content
=
mu
.
get
(
'content'
,{})
'taoassistant_single_product'
,
'taoassistant_single_product_v2'
):
mu_content_cardData
=
mu_content
.
get
(
'cardData'
,{})
mu_content
=
mu
.
get
(
'content'
,
{})
mu_content_cardData_data
=
mu_content_cardData
.
get
(
'data'
,{})
mu_content_cardData
=
mu_content
.
get
(
'cardData'
,
{})
mu_content_cardData_data
=
mu_content_cardData
.
get
(
'data'
,
{})
if
mu_content_cardData_data
:
if
mu_content_cardData_data
:
source_seq
=
mu_content
.
get
(
'cardId'
)
or
mu_content
.
get
(
'businessId'
)
source_seq
=
mu_content
.
get
(
'cardId'
)
or
mu_content
.
get
(
'businessId'
)
price
=
(
mu_content_cardData_data
.
get
(
'priceShowWithIcon'
)
or
{})
.
get
(
'price'
)
or
mu_content_cardData_data
.
get
(
price
=
(
mu_content_cardData_data
.
get
(
'priceShowWithIcon'
)
or
{})
.
get
(
'price'
)
or
mu_content_cardData_data
.
get
(
'itemPrice'
)
or
''
'itemPrice'
)
or
''
taobao_list
.
append
({
taobao_list
.
append
({
"title"
:
mu_content_cardData_data
.
get
(
'title'
),
"title"
:
mu_content_cardData_data
.
get
(
'title'
),
...
@@ -286,7 +336,8 @@ def qianwen_android_process_original_data(task_data):
...
@@ -286,7 +336,8 @@ def qianwen_android_process_original_data(task_data):
(
(
item
.
get
(
'text'
,
''
)
item
.
get
(
'text'
,
''
)
for
item
in
for
item
in
mu_content_cardData_data
.
get
(
'structuredShopInfo'
,
{})
.
get
(
'infoList'
,
[])
mu_content_cardData_data
.
get
(
'structuredShopInfo'
,
{})
.
get
(
'infoList'
,
[])
if
item
.
get
(
'sourceType'
)
==
'shop_name'
if
item
.
get
(
'sourceType'
)
==
'shop_name'
),
),
''
''
...
@@ -300,13 +351,12 @@ def qianwen_android_process_original_data(task_data):
...
@@ -300,13 +351,12 @@ def qianwen_android_process_original_data(task_data):
'card_type'
:
'single_product'
'card_type'
:
'single_product'
})
})
if
taobao_list
:
if
taobao_list
:
response_content
+=
'render_ecom_card_widget_taobao_start:'
response_content
+=
'render_ecom_card_widget_taobao_start:'
damai_str
=
json
.
dumps
(
taobao_list
,
ensure_ascii
=
False
)
damai_str
=
json
.
dumps
(
taobao_list
,
ensure_ascii
=
False
)
response_content
+=
damai_str
response_content
+=
damai_str
response_content
+=
'render_ecom_card_widget_taobao_end:
\n
'
response_content
+=
'render_ecom_card_widget_taobao_end:
\n
'
save_eco_data_to_bh
(
task_data
,
'taobao'
,
taobao_list
)
save_eco_data_to_bh
(
task_data
,
'taobao'
,
taobao_list
)
damai_list
=
[]
damai_list
=
[]
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'damai_shows_list'
and
multi_load_status
==
'complete'
:
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'damai_shows_list'
and
multi_load_status
==
'complete'
:
...
@@ -335,6 +385,7 @@ def qianwen_android_process_original_data(task_data):
...
@@ -335,6 +385,7 @@ def qianwen_android_process_original_data(task_data):
'id'
:
damai
.
get
(
'data'
)
.
get
(
'id'
)
'id'
:
damai
.
get
(
'data'
)
.
get
(
'id'
)
})
})
damai_data_list
.
extend
(
damai_list
)
damai_data_list
.
extend
(
damai_list
)
gaode_list
=
[]
gaode_list
=
[]
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'new_amap_poi_list'
and
status
==
'complete'
:
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'new_amap_poi_list'
and
status
==
'complete'
:
for
mu
in
multi_load
:
for
mu
in
multi_load
:
...
@@ -369,13 +420,14 @@ def qianwen_android_process_original_data(task_data):
...
@@ -369,13 +420,14 @@ def qianwen_android_process_original_data(task_data):
gaode_data_list
.
extend
(
gaode_list
)
gaode_data_list
.
extend
(
gaode_list
)
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'new_amap_poi_list'
and
(
multi_load_status
==
'complete'
):
if
mime_type
==
'multi_load/iframe'
and
multi_load_type
==
'new_amap_poi_list'
and
(
multi_load_status
==
'complete'
):
for
mu
in
multi_load
:
for
mu
in
multi_load
:
mu_content
=
mu
.
get
(
'content'
)
mu_content
=
mu
.
get
(
'content'
)
mu_source_seq
=
mu
.
get
(
'source_seq'
)
mu_source_seq
=
mu
.
get
(
'source_seq'
)
mu_content_model_input
=
mu_content
.
get
(
'modelInput'
)
mu_content_model_input
=
mu_content
.
get
(
'modelInput'
)
mu_content_pois
=
mu_content
.
get
(
'pois'
)
mu_content_pois
=
mu_content
.
get
(
'pois'
)
for
poi
in
mu_content_pois
:
for
poi
in
mu_content_pois
:
poi_summary
=
poi
.
get
(
'summary1'
)
poi_summary
=
poi
.
get
(
'summary1'
)
...
@@ -386,15 +438,17 @@ def qianwen_android_process_original_data(task_data):
...
@@ -386,15 +438,17 @@ def qianwen_android_process_original_data(task_data):
poi_id
=
mu_content_model_input
.
get
(
"poi_id"
)
poi_id
=
mu_content_model_input
.
get
(
"poi_id"
)
gaode_result
=
{
gaode_result
=
{
"url"
:
f
"https://www.amap.com/place/{poi_id}"
,
"url"
:
f
"https://www.amap.com/place/{poi_id}"
,
"source_seq"
:
mu_source_seq
,
"source_seq"
:
mu_source_seq
,
"name"
:
mu_content_model_input
.
get
(
"name"
),
"name"
:
mu_content_model_input
.
get
(
"name"
),
"poi_id"
:
poi_id
,
"poi_id"
:
poi_id
,
"cost"
:
cost
,
"cost"
:
cost
,
"poi_summary"
:
poi_summary
,
"poi_summary"
:
poi_summary
,
"distance_formatted"
:
distance_formatted
,
"distance_formatted"
:
distance_formatted
,
"photos"
:
photos
[
0
]
.
get
(
"url"
,
""
)
if
isinstance
(
photos
,
list
)
and
photos
and
isinstance
(
photos
[
0
],
dict
)
else
""
,
"photos"
:
photos
[
0
]
.
get
(
"url"
,
""
)
if
isinstance
(
photos
,
"address"
:
distance_address
,
list
)
and
photos
and
isinstance
(
photos
[
0
],
dict
)
else
""
,
"address"
:
distance_address
,
}
}
gaode_list
.
append
(
gaode_result
)
gaode_list
.
append
(
gaode_result
)
gaode_data_list
.
extend
(
gaode_list
)
gaode_data_list
.
extend
(
gaode_list
)
...
@@ -413,6 +467,25 @@ def qianwen_android_process_original_data(task_data):
...
@@ -413,6 +467,25 @@ def qianwen_android_process_original_data(task_data):
think_content
+=
multi_load_content
.
get
(
'think_content'
)
think_content
+=
multi_load_content
.
get
(
'think_content'
)
# 引用来源 回答 视频列表
# 引用来源 回答 视频列表
if
mime_type
==
'multi_load/iframe'
and
status
==
'complete'
:
if
mime_type
==
'multi_load/iframe'
and
status
==
'complete'
:
data_extra_info
=
data
.
get
(
'extra_info'
)
if
data_extra_info
:
data_extra_info_chat_odps
=
data_extra_info
.
get
(
'chat_odps'
)
if
data_extra_info_chat_odps
:
data_material_detail
=
data_extra_info_chat_odps
.
get
(
'material_detail'
)
if
data_material_detail
:
data_material_detail_query
=
data_material_detail
.
get
(
'query_info'
)
data_material_final_material_detail
=
data_material_detail
.
get
(
'final_material_detail'
)
if
data_material_final_material_detail
:
url_list
=
process_source
(
data_material_final_material_detail
)
if
data_material_detail_query
:
rewrite_querys
=
data_material_detail_query
.
get
(
'rewrite_querys'
)
if
isinstance
(
rewrite_querys
,
list
):
search_keyword
.
extend
(
rewrite_querys
)
response_content
+=
content
response_content
+=
content
for
mu
in
multi_load
:
for
mu
in
multi_load
:
if
mu
.
get
(
'type'
)
==
'video_note_list'
:
if
mu
.
get
(
'type'
)
==
'video_note_list'
:
...
@@ -473,17 +546,19 @@ def qianwen_android_process_original_data(task_data):
...
@@ -473,17 +546,19 @@ def qianwen_android_process_original_data(task_data):
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
#https://tcdn.aidso.com/geo/6fede9d0-ce6c-4c97-bcb8-43c499fc24e5/TYQWA/think.txt?secret=1104ed37df6abefbb2b454530b06515f0f59505d5751504e524855
#
https://tcdn.aidso.com/geo/6fede9d0-ce6c-4c97-bcb8-43c499fc24e5/TYQWA/think.txt?secret=1104ed37df6abefbb2b454530b06515f0f59505d5751504e524855
# 6fede9d0-ce6c-4c97-bcb8-43c499fc24e5
# 6fede9d0-ce6c-4c97-bcb8-43c499fc24e5
# file_path2 = 'geo/alskdjhfasldjkfh/TYQWA/original.text'
# file_path2 = 'geo/alskdjhfasldjkfh/TYQWA/original.text'
# file_path2 = 'geo/ef66d5f9-cadb-43f3-b8cf-824d75a81e7c/TYQWA/original.text'
# file_path2 = 'geo/ef66d5f9-cadb-43f3-b8cf-824d75a81e7c/TYQWA/original.text'
# qianwen_android_process_original_data(file_path2)
# qianwen_android_process_original_data(file_path2)
#
fb8de08c16484143a7299c06ba20c90d 老版本
#
8a5d2d78-acca-4621-a913-1ff1527f8789
#
e09e1b7db91f4d0382e86458d8462147 新版本
#
04b2b061-a5c0-4429-8d3b-d2b6504f1e8b
# data_list = bh_utils.query_data(f"select * from geo_commit_task where taskId = '0001e01d-a8ea-405e-acef-93e4f55abbff' and platform = 'TYQWA'")
# data_list = bh_utils.query_data(f"select * from geo_commit_task where taskId = '0001e01d-a8ea-405e-acef-93e4f55abbff' and platform = 'TYQWA'")
data_list
=
bh_utils
.
query_data
(
f
"select * from geo_commit_task where taskId = '53f0d16d0fa64d3e89c4b9af7aae86e8' and platform = 'TYQWA'"
)
data_list
=
bh_utils
.
query_data
(
f
"select * from geo_commit_task where taskId = '9b82af12-97ba-47e0-aaca-4d88b93c70e9' and platform = 'TYQWA'"
)
def
handle_item
(
i
):
def
handle_item
(
i
):
...
...
aidso_geo/models/spider_save_tos.py
View file @
27ac512a
...
@@ -265,12 +265,12 @@ def qianwen_android_process_quote(url_list):
...
@@ -265,12 +265,12 @@ def qianwen_android_process_quote(url_list):
content
=
url
.
get
(
'content'
)
content
=
url
.
get
(
'content'
)
if
content
:
if
content
:
if
isinstance
(
content
.
get
(
'list'
),
list
):
if
isinstance
(
content
.
get
(
'list'
),
list
):
for
c
in
content
.
get
(
'list'
):
for
index2
,
c
in
enumerate
(
content
.
get
(
'list'
),
start
=
1
):
raw_data
=
{
raw_data
=
{
"url"
:
c
.
get
(
'url'
,
''
),
"url"
:
c
.
get
(
'url'
,
''
),
"title"
:
c
.
get
(
'title'
,
''
),
"title"
:
c
.
get
(
'title'
,
''
),
"snippet"
:
c
.
get
(
'summary'
,
''
),
"snippet"
:
c
.
get
(
'summary'
,
''
),
"index"
:
index
,
"index"
:
index
2
,
"published_at"
:
c
.
get
(
'publish_time'
,
''
),
"published_at"
:
c
.
get
(
'publish_time'
,
''
),
"site_name"
:
c
.
get
(
'name'
,
''
),
"site_name"
:
c
.
get
(
'name'
,
''
),
"site_icon"
:
c
.
get
(
'icon'
,
''
),
"site_icon"
:
c
.
get
(
'icon'
,
''
),
...
@@ -624,6 +624,3 @@ def process_and_save_files_ai(file_path, search_keyword, url_list, think_content
...
@@ -624,6 +624,3 @@ def process_and_save_files_ai(file_path, search_keyword, url_list, think_content
for
content
,
file_name
in
data_config
:
for
content
,
file_name
in
data_config
:
save_data_to_tos_ai
(
target_dir
,
content
,
file_name
)
save_data_to_tos_ai
(
target_dir
,
content
,
file_name
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment