179 lines
6.0 KiB
Bash
Executable File
179 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
||
# 课时完成记录查询脚本
|
||
# 查询指定课时(chapter_id)的首次完成记录,包含角色信息、最近登录、课程耗时
|
||
# 数据库:线上 PostgreSQL (vala_bi)
|
||
# 输出:xlsx 格式
|
||
# 用法:bash scripts/query_chapter_play.sh
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
source "${SCRIPT_DIR}/../secrets.env"
|
||
|
||
DB_HOST="bj-postgres-16pob4sg.sql.tencentcdb.com"
|
||
DB_PORT="28591"
|
||
DB_USER="ai_member"
|
||
DB_NAME="vala_bi"
|
||
|
||
OUTPUT_DIR="${SCRIPT_DIR}/../output"
|
||
mkdir -p "$OUTPUT_DIR"
|
||
|
||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||
CSV_TMP="${OUTPUT_DIR}/.chapter_play_${TIMESTAMP}.csv"
|
||
OUTPUT_FILE="${OUTPUT_DIR}/chapter_play_${TIMESTAMP}.xlsx"
|
||
|
||
SQL=$(cat <<'EOF'
|
||
WITH chapter_play AS (
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at) AS finish_date
|
||
FROM bi_user_chapter_play_record_0
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_1
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_2
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_3
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_4
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_5
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_6
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, chapter_unique_id, date(updated_at)
|
||
FROM bi_user_chapter_play_record_7
|
||
WHERE chapter_id IN (55,56,57,58,59,343,344,345,346,348) AND play_status = 1
|
||
),
|
||
comp_time AS (
|
||
SELECT chapter_unique_id, SUM(interval_time) AS total_interval
|
||
FROM (
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_0
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_1
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_2
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_3
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_4
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_5
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_6
|
||
UNION ALL
|
||
SELECT chapter_unique_id, interval_time FROM bi_user_component_play_record_7
|
||
) t
|
||
GROUP BY chapter_unique_id
|
||
),
|
||
course_detail AS (
|
||
SELECT
|
||
cp.user_id,
|
||
cp.chapter_id,
|
||
FORMAT('%s-%s-%s-%s', l.course_level, l.course_season, l.course_unit, l.course_lesson) AS course_id,
|
||
cp.finish_date,
|
||
FORMAT('%s:%s',
|
||
FLOOR(ct.total_interval / 1000 / 60),
|
||
LPAD(CAST(MOD(ct.total_interval / 1000, 60) AS TEXT), 2, '0')
|
||
) AS finish_time
|
||
FROM (
|
||
SELECT user_id, chapter_id, chapter_unique_id, finish_date,
|
||
ROW_NUMBER() OVER (PARTITION BY user_id, chapter_id ORDER BY finish_date) AS rn
|
||
FROM chapter_play
|
||
) cp
|
||
LEFT JOIN bi_level_unit_lesson l ON cp.chapter_id = l.id
|
||
LEFT JOIN comp_time ct ON cp.chapter_unique_id = ct.chapter_unique_id
|
||
WHERE cp.rn = 1
|
||
)
|
||
|
||
SELECT
|
||
a.account_id AS "账号ID",
|
||
a.id AS "角色ID",
|
||
DATE(a.created_at) AS "注册日期",
|
||
DATE(TO_TIMESTAMP(b.latest_active_time)) AS "最近登录时间",
|
||
c.course_id AS "课程名称",
|
||
c.finish_date AS "课程完成时间",
|
||
c.finish_time AS "课程耗时"
|
||
FROM bi_vala_app_character a
|
||
LEFT JOIN user_detail_info b ON a.id = b.user_id
|
||
LEFT JOIN course_detail c ON a.id = c.user_id
|
||
WHERE a.status = 1
|
||
ORDER BY a.id, c.course_id;
|
||
EOF
|
||
)
|
||
|
||
echo "正在查询..."
|
||
PGPASSWORD="$PG_ONLINE_PASSWORD" psql \
|
||
-h "$DB_HOST" \
|
||
-p "$DB_PORT" \
|
||
-U "$DB_USER" \
|
||
-d "$DB_NAME" \
|
||
--csv \
|
||
-c "$SQL" \
|
||
> "$CSV_TMP"
|
||
|
||
ROW_COUNT=$(tail -n +2 "$CSV_TMP" | wc -l)
|
||
echo "查询完成,共 ${ROW_COUNT} 条记录,正在转换为 xlsx..."
|
||
|
||
python3 - "$CSV_TMP" "$OUTPUT_FILE" <<'PYEOF'
|
||
import csv, sys
|
||
from openpyxl import Workbook
|
||
from openpyxl.styles import Font, Alignment, PatternFill
|
||
|
||
csv_path, xlsx_path = sys.argv[1], sys.argv[2]
|
||
|
||
wb = Workbook()
|
||
ws = wb.active
|
||
ws.title = "课时完成记录"
|
||
|
||
# 读取 CSV
|
||
with open(csv_path, 'r', encoding='utf-8') as f:
|
||
reader = csv.reader(f)
|
||
for row in reader:
|
||
ws.append(row)
|
||
|
||
# 表头样式
|
||
header_font = Font(bold=True, color="FFFFFF", size=11)
|
||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||
header_align = Alignment(horizontal="center", vertical="center")
|
||
|
||
for cell in ws[1]:
|
||
cell.font = header_font
|
||
cell.fill = header_fill
|
||
cell.alignment = header_align
|
||
|
||
# 自动列宽
|
||
for col in ws.columns:
|
||
max_len = 0
|
||
col_letter = col[0].column_letter
|
||
for cell in col:
|
||
val = str(cell.value) if cell.value else ""
|
||
# 中文字符算 2 宽度
|
||
width = sum(2 if ord(c) > 127 else 1 for c in val)
|
||
if width > max_len:
|
||
max_len = width
|
||
ws.column_dimensions[col_letter].width = min(max_len + 3, 30)
|
||
|
||
# 冻结首行
|
||
ws.freeze_panes = "A2"
|
||
|
||
wb.save(xlsx_path)
|
||
print(f"xlsx 已保存: {xlsx_path}")
|
||
PYEOF
|
||
|
||
# 清理临时 CSV
|
||
rm -f "$CSV_TMP"
|
||
|
||
echo "完成!结果已保存至: ${OUTPUT_FILE}"
|