ai_member_xiaoxi/scripts/run_user_course_stat.sh
2026-05-22 08:00:01 +08:00

152 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 用户购课完课统计定时任务脚本
# 执行时间每天9:00
# 输出Excel报表发送到指定群
set -euo pipefail
# 配置参数
DB_HOST="bj-postgres-16pob4sg.sql.tencentcdb.com"
DB_PORT="28591"
DB_NAME="vala_bi"
DB_USER="ai_member"
SQL_PATH="/root/.openclaw/workspace/scripts/用户购课完课统计.sql"
OUTPUT_CSV="/tmp/user_course_stat_$(date +%Y%m%d).csv"
OUTPUT_EXCEL="/root/.openclaw/workspace/output/用户购课完课统计_$(date +%Y%m%d).xlsx"
TARGET_CHAT_ID="oc_af81515caefe26918736ad1941286224"
LOG_FILE="/var/log/user_course_stat.log"
# 确保 PATH 包含必要工具
export PATH="/usr/bin:/bin:/usr/local/bin:/root/.nvm/versions/node/v24.14.0/bin:$PATH"
# 加载数据库密码
source /root/.openclaw/workspace/secrets.env
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" >&2; }
log "========== 开始执行用户购课完课统计任务 =========="
# 1. 运行SQL导出CSV
log "执行SQL查询..."
export PGPASSWORD="${PG_ONLINE_PASSWORD}"
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" --csv -f "$SQL_PATH" > "$OUTPUT_CSV" 2>> "$LOG_FILE"
if [ ! -s "$OUTPUT_CSV" ]; then
log "ERROR: SQL查询结果为空"
exit 1
fi
log "SQL查询完成CSV大小: $(wc -c < "$OUTPUT_CSV") bytes"
# 2. CSV转Excel
log "生成Excel..."
python3 -c "
import pandas as pd
import numpy as np
try:
np._get_promotion_state = lambda *args, **kwargs: 0
except:
pass
df = pd.read_csv('${OUTPUT_CSV}', low_memory=False)
with pd.ExcelWriter('${OUTPUT_EXCEL}') as writer:
df.to_excel(writer, sheet_name='用户统计数据', index=False)
" 2>> "$LOG_FILE"
if [ ! -f "$OUTPUT_EXCEL" ]; then
log "ERROR: Excel生成失败"
exit 1
fi
log "Excel生成完成大小: $(wc -c < "$OUTPUT_EXCEL") bytes"
# 3. 获取飞书token
log "获取飞书访问令牌..."
APP_ID=$(jq -r '.apps[0].appId' /root/.openclaw/credentials/xiaoxi/config.json)
APP_SECRET=$(jq -r '.apps[0].appSecret' /root/.openclaw/credentials/xiaoxi/config.json)
TOKEN=$(curl -s -X POST "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" \
-H "Content-Type: application/json" \
-d "{\"app_id\":\"$APP_ID\",\"app_secret\":\"$APP_SECRET\"}" \
| jq -r '.tenant_access_token')
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
log "ERROR: 获取飞书token失败"
exit 1
fi
# 4. 先发文本消息
log "发送文本通知..."
TEXT_PAYLOAD=$(jq -n --arg rid "$TARGET_CHAT_ID" --arg text "【每日定时统计】$(date +%Y年%m月%d日)用户购课与完课情况统计,请查收附件。" \
'{receive_id: $rid, msg_type: "text", content: ({text: $text} | tostring)}')
TEXT_RESP=$(curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$TEXT_PAYLOAD")
TEXT_CODE=$(echo "$TEXT_RESP" | jq -r '.code')
if [ "$TEXT_CODE" != "0" ]; then
log "WARN: 文本消息发送失败: $TEXT_RESP"
fi
# 5. 上传Excel文件带重试
upload_file() {
local attempt=1
local max_attempts=3
while [ $attempt -le $max_attempts ]; do
log "上传文件 (第${attempt}次尝试)..."
local fk
fk=$(curl -s --max-time 30 -X POST "https://open.feishu.cn/open-apis/im/v1/files" \
-H "Authorization: Bearer $TOKEN" \
-F "file_type=xls" \
-F "file_name=用户购课完课统计_$(date +%Y%m%d).xlsx" \
-F "file=@${OUTPUT_EXCEL}" | jq -r '.data.file_key')
if [ -n "$fk" ] && [ "$fk" != "null" ]; then
echo "$fk"
return 0
fi
log "WARN: 第${attempt}次文件上传失败等待3秒后重试..."
sleep 3
attempt=$((attempt + 1))
done
return 1
}
FILE_KEY=$(upload_file)
if [ -z "$FILE_KEY" ] || [ "$FILE_KEY" = "null" ]; then
log "ERROR: 文件上传失败已重试3次"
exit 1
fi
log "文件上传成功file_key: ${FILE_KEY}"
# 6. 发送文件消息到群(带重试)
send_file_msg() {
local attempt=1
local max_attempts=3
local fk="$1"
while [ $attempt -le $max_attempts ]; do
log "发送文件消息 (第${attempt}次尝试)..."
local payload
payload=$(jq -n --arg rid "$TARGET_CHAT_ID" --arg fk "$fk" \
'{receive_id: $rid, msg_type: "file", content: ({file_key: $fk} | tostring)}')
local resp
resp=$(curl -s --max-time 15 -X POST "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$payload")
local code
code=$(echo "$resp" | jq -r '.code')
if [ "$code" = "0" ]; then
log "文件消息发送成功"
return 0
fi
log "WARN: 第${attempt}次文件消息发送失败 (code=${code}): $resp"
sleep 3
attempt=$((attempt + 1))
done
return 1
}
if ! send_file_msg "$FILE_KEY"; then
log "ERROR: 文件消息发送失败已重试3次"
exit 1
fi
# 7. 清理临时CSV文件
rm -f "$OUTPUT_CSV"
log "========== 任务执行完成 =========="