cela-自动学习脚本API版

[API版] cela自动学习脚本,支持浦东分院课程列表页面,支持专栏详情页面课程获取,修复进度上报API端点,基于真实API分析优化。

// ==UserScript==
// @name         cela-自动学习脚本API版
// @namespace    https://github.com/Moker32/
// @version      3.37.4
// @description  [API版] cela自动学习脚本,支持浦东分院课程列表页面,支持专栏详情页面课程获取,修复进度上报API端点,基于真实API分析优化。
// @author       Moker32
// @license      GPL-3.0-or-later
// @changelog    v3.37.4: 移除UI配置功能,使用固定默认配置:智能学习模式、跳过已完成课程、启用学习记录、关闭调试模式
// @grant        GM_getValue
// @grant        GM_setValue
// @match        *://cela.e-celap.cn/*
// @match        *://pudong.e-celap.cn/*
// @match        *://pd.cela.cn/*
// @match        *://*.e-celap.cn/*
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @connect      cela.e-celap.cn
// @connect      pudong.e-celap.cn
// @connect      pd.cela.cn
// @connect      zpyapi.shsets.com
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- 常量集中管理 ---
    const CONSTANTS = {
        API_ENDPOINTS: {
            GET_PLAY_TREND: '/inc/nc/course/play/getPlayTrend',
            PULSE_SAVE_RECORD: '/inc/nc/course/play/pulseSaveRecord',
            REPORT_PROGRESS: '/inc/nc/course/play/reportProgress',
            UPDATE_PROGRESS: '/inc/nc/course/play/updateProgress',
            START_STUDY: '/inc/nc/course/play/startStudy',
            FINISH_STUDY: '/inc/nc/course/play/finishStudy',
            COMPLETE_COURSE: '/inc/nc/course/completeCourse',
            FINISH_LEARNING: '/inc/nc/course/finishLearning',
            UPDATE_STATUS: '/inc/nc/course/updateStatus',
            SUBMIT_LEARNING_RECORD: '/inc/nc/course/submitLearningRecord',
            GRADUATE: '/inc/nc/course/graduate',
            GET_STUDY_RECORD: '/inc/nc/course/getStudyRecord',
            CREATE_SESSION: '/inc/nc/course/createSession',
            INIT_STUDY_RECORD: '/inc/nc/course/initStudyRecord',
            COMPLETE_LEARNING: '/inc/nc/course/completeLearning',
            SAVE_COMPLETION_STATUS: '/inc/nc/course/saveCompletionStatus',
            SAVE_STUDY_RECORD: '/inc/nc/course/saveStudyRecord',
            GET_COURSEWARE_DETAIL: '/inc/nc/course/play/getCoursewareDetail',
            GET_PACK_BY_ID: '/inc/nc/pack/getById',
            GET_COURSE_LIST: '/api/course/list',
            PUSH_COURSE: '/dsfa/nc/cela/api/pushCourse',
            GET_COURSE_BY_USER: '/dsfa/nc/cela/api/getCourseByUserAndCourse',
            VIDEO_PROGRESS: '/api/player/progress'
        },
        SELECTORS: {
            PANEL: '#api-learner-panel',
            STATUS_DISPLAY: '#learner-status',
            PROGRESS_INNER: '#learner-progress-inner',
            TOGGLE_BTN: '#toggle-learning-btn',
            LOG_CONTAINER: '#api-learner-panel .log-container',
            STAT_TOTAL: '#stat-total',
            STAT_COMPLETED: '#stat-completed',
            STAT_LEARNED: '#stat-learned',
            STAT_SKIPPED: '#stat-skipped',
            APP: '#app'
        },
        STORAGE_KEYS: {
            TOKEN: 'token',
            AUTH_TOKEN: 'authToken',
            ACCESS_TOKEN: 'access_token',
            USER_ID: 'userId',
            USER_ID_ALT: 'user_id'
        },
        COURSE_SELECTORS: [
            '[class*="course"]', '[data-course]', '.course-item', '.lesson-item',
            '.el-card', '.el-card__body', '.course-card', '.course-box',
            '.nc-course-item', '.study-item', '.learn-item',
            '[class*="item"]', '[class*="card"]', '[data-id]',
            '.pudong-course', '.pd-course', '.dsf-course'
        ],
        FALLBACK_SELECTORS: [
            'div[class*="list"] > div',
            'ul > li',
            '.content div',
            '#app div[class]',
            '[class*="container"] > div'
        ],
        VIDEO_SELECTORS: [
            'video', '[api-base-url]', '[class*="video"]', 'iframe[src*="play"]'
        ],
        COOKIE_PATTERNS: {
            USER_ID: /userId=([^;]+)/,
            TOKEN: /token=([^;]+)/,
            P_PARAM: /_p=([^;]+)/
        },
        TIME_FORMATS: {
            DEFAULT_DURATION: 1800, // 30分钟
            MOCK_DURATION: 1800
        },
        UI_LIMITS: {
            MAX_LOG_ENTRIES: 50,
            LOG_FLUSH_DELAY: 100
        }
    };

    // --- 事件驱动机制 (v2.0优化) ---
    const EventBus = {
        events: {},
        
        subscribe(event, listener) {
            if (!this.events[event]) {
                this.events[event] = [];
            }
            this.events[event].push(listener);
            return () => {
                // 返回取消订阅函数
                const index = this.events[event].indexOf(listener);
                if (index > -1) {
                    this.events[event].splice(index, 1);
                }
            };
        },
        
        publish(event, data) {
            if (!this.events[event]) return;
            this.events[event].forEach(listener => {
                try {
                    listener(data);
                } catch (error) {
                    console.error(`EventBus error in ${event}:`, error);
                }
            });
        },
        
        // 一次性事件监听
        once(event, listener) {
            const unsubscribe = this.subscribe(event, (data) => {
                unsubscribe();
                listener(data);
            });
            return unsubscribe;
        }
    };

    // --- 配置管理模块 (v2.0优化) ---
    const Settings = {
        defaultConfig: {
            // === 固定配置 (不可修改) ===
            LEARNING_STRATEGY: 'smart',                    // 智能学习模式
            SKIP_COMPLETED_COURSES: true,                  // 跳过已完成课程
            STUDY_RECORD_ENABLED: true,                    // 启用学习记录
            FALLBACK_MODE: true,                           // 启用兜底模式
            DEBUG_MODE: false,                             // 关闭调试模式
            HEARTBEAT_INTERVAL: 10,                        // 进度上报间隔(秒)
            COMPLETION_THRESHOLD: 95,                      // 完成度阈值(%)
            
            // === 技术配置 (高级选项) ===
            MAX_RETRY_ATTEMPTS: 10,                        // 最大重试次数
            RETRY_DELAY: 3000,                            // 重试延迟(毫秒)
            COURSE_COMPLETION_DELAY: 5,                    // 课程完成延迟(秒)
            
            // === 自动配置 (系统检测) ===
            PUDONG_MODE: false,                           // 浦东分院模式(自动检测)
            PUDONG_API_BASE: '',                          // 浦东API基础URL(自动设置)
            
            // === 向后兼容配置 (将被迁移) ===
            FAST_LEARNING_MODE: true,                     // 兼容旧版本
            ULTRA_FAST_MODE: true,                        // 兼容旧版本
            SMART_LEARNING_MODE: true                     // 兼容旧版本
        },
        
        config: {},
        
        load() {
            // 使用固定配置,不再从存储加载
            this.config = { ...this.defaultConfig };
            EventBus.publish('log', { message: '✅ 使用固定配置:智能学习模式', type: 'success' });
        },
        
        get(key) {
            return this.config[key];
        }
    };

    // --- 学习策略枚举 (v3.37.3优化) ---
    const LEARNING_STRATEGIES = {
        SLOW: 'slow',           // 慢启动 - 模拟真实学习
        NORMAL: 'normal',       // 正常速度 - 平衡效率与安全
        FAST: 'fast',          // 快速学习 - 提高学习效率
        ULTRA: 'ultra',        // 超快速 - 直接完成
        SMART: 'smart'         // 智能自适应 - 根据环境调整
    };

    // --- 配置区域 (v3.37.4简化) ---
    const CONFIG = new Proxy({}, {
        get(target, prop) {
            // 优先从Settings获取配置,如果不存在则使用默认值
            return Settings.get(prop) ?? target[prop];
        },
        set(target, prop, value) {
            // 固定配置模式,直接设置到target
            target[prop] = value;
            return true;
        }
    });

    // 自动检测当前环境
    const detectEnvironment = () => {
        const hostname = window.location.hostname;
        const href = window.location.href;
        
        // 检测是否为浦东分院
        if (hostname.includes('pudong') || 
            hostname.includes('pd.') || 
            href.includes('浦东分院') ||
            href.includes('pudong') ||
            document.title.includes('浦东')) {
            CONFIG.PUDONG_MODE = true;
            console.log('🏢 检测到浦东分院环境');
        }
        
        // 设置API基础URL
        if (CONFIG.PUDONG_MODE) {
            if (hostname.includes('pudong.e-celap.cn')) {
                CONFIG.PUDONG_API_BASE = `https://${hostname}`;
            } else if (hostname.includes('pd.cela.cn')) {
                CONFIG.PUDONG_API_BASE = `https://${hostname}`;
            } else {
                // 默认使用主站API
                CONFIG.PUDONG_API_BASE = 'https://cela.e-celap.cn';
            }
        }
        
        console.log(`🌐 当前环境: ${CONFIG.PUDONG_MODE ? '浦东分院' : '主站'} (${hostname})`);
        console.log(`🔗 API基础URL: ${CONFIG.PUDONG_API_BASE || 'https://cela.e-celap.cn'}`);
    };

    // --- UI和日志(优化版) ---
    const UI = {
        logs: [],
        logBuffer: [], // 日志缓冲区
        logUpdateTimeout: null,
        statistics: {
            total: 0,
            completed: 0,
            learned: 0,
            failed: 0,
            skipped: 0
        },
        createPanel: () => {
            const panel = document.createElement('div');
            panel.id = 'api-learner-panel';
            panel.innerHTML = `
                <div class="header">
                    API学习助手 v3.37.4
                </div>
                <div class="content">
                    <div class="status">状态: <span id="learner-status">待命</span></div>
                    <div class="statistics">
                        <div class="stat-item">总计: <span id="stat-total">0</span></div>
                        <div class="stat-item">已完成: <span id="stat-completed">0</span></div>
                        <div class="stat-item">新学习: <span id="stat-learned">0</span></div>
                        <div class="stat-item">跳过: <span id="stat-skipped">0</span></div>
                    </div>
                    <div class="progress-bar"><div id="learner-progress-inner"></div></div>
                    
                    <div class="log-container"></div>
                </div>
                <div class="footer">
                    <button id="toggle-learning-btn" data-state="stopped">开始学习</button>
                    <div class="feature-note">✨ 智能学习模式 + 自动记录</div>
                </div>
            `;
            document.body.appendChild(panel);
            UI.addStyles();
            UI.initEventListeners();
        },
        // 优化后的日志函数 - 使用批量更新策略
        log: function(message, type = 'info') {
            const timestamp = new Date().toLocaleTimeString();
            const logMessage = `[${timestamp}] ${message}`;
            
            // 添加到缓冲区
            this.logBuffer.push({ message: logMessage, type });

            // 使用防抖处理,批量更新DOM
            if (this.logUpdateTimeout) clearTimeout(this.logUpdateTimeout);
            this.logUpdateTimeout = setTimeout(() => this.flushLogBuffer(), CONSTANTS.UI_LIMITS.LOG_FLUSH_DELAY);
            
            if (CONFIG.DEBUG_MODE) {
                const debugMessage = `[API Learner Debug] ${logMessage}`;
                console.log(debugMessage);
                this.logs.push(debugMessage);
            }
        },

        // 初始化事件监听器 (v2.0新增)
        initEventListeners: function() {
            // 订阅事件
            EventBus.subscribe('log', ({ message, type }) => this.log(message, type));
            EventBus.subscribe('statusUpdate', status => this.updateStatus(status));
            EventBus.subscribe('progressUpdate', progress => this.updateProgress(progress));
            EventBus.subscribe('statisticsUpdate', stats => this.updateStatistics(stats));
        },
        // 批量刷新日志缓冲区
        flushLogBuffer: function() {
            const logContainer = document.querySelector(CONSTANTS.SELECTORS.LOG_CONTAINER);
            if (!logContainer || this.logBuffer.length === 0) return;

            const fragment = document.createDocumentFragment();
            this.logBuffer.forEach(log => {
                const logEntry = document.createElement('div');
                logEntry.className = `log-entry ${log.type}`;
                logEntry.textContent = log.message;
                fragment.appendChild(logEntry);
            });

            logContainer.appendChild(fragment);
            logContainer.scrollTop = logContainer.scrollHeight;
            
            // 限制日志条数,避免占用过多内存
            const entries = logContainer.querySelectorAll('.log-entry');
            if (entries.length > CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES) {
                for (let i = 0; i < entries.length - CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES; i++) {
                    entries[i].remove();
                }
            }
            
            this.logBuffer = []; // 清空缓冲区
        },
        updateStatus: (status) => {
            const statusEl = document.getElementById(CONSTANTS.SELECTORS.STATUS_DISPLAY.replace('#', ''));
            if (statusEl) statusEl.textContent = status;
        },
        updateProgress: (percentage) => {
            const progressInner = document.getElementById(CONSTANTS.SELECTORS.PROGRESS_INNER.replace('#', ''));
            if (progressInner) progressInner.style.width = `${percentage}%`;
        },
        updateStatistics: (stats) => {
            Object.assign(UI.statistics, stats);
            document.getElementById(CONSTANTS.SELECTORS.STAT_TOTAL.replace('#', '')).textContent = UI.statistics.total;
            document.getElementById(CONSTANTS.SELECTORS.STAT_COMPLETED.replace('#', '')).textContent = UI.statistics.completed;
            document.getElementById(CONSTANTS.SELECTORS.STAT_LEARNED.replace('#', '')).textContent = UI.statistics.learned;
            document.getElementById(CONSTANTS.SELECTORS.STAT_SKIPPED.replace('#', '')).textContent = UI.statistics.skipped;
        },
        addStyles: () => {
            const styles = `
                #api-learner-panel { position: fixed; bottom: 20px; right: 20px; width: 400px; background: #fff; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 99999; font-family: sans-serif; font-size: 14px; color: #333; }
                #api-learner-panel .header { background: #f7f7f7; padding: 10px 15px; font-weight: bold; border-bottom: 1px solid #ddd; border-radius: 8px 8px 0 0; }
                #api-learner-panel .content { padding: 15px; }
                #api-learner-panel .status { margin-bottom: 10px; }
                #api-learner-panel .statistics { display: flex; justify-content: space-between; margin-bottom: 10px; padding: 8px; background: #f9f9f9; border-radius: 4px; font-size: 12px; }
                #api-learner-panel .stat-item { text-align: center; }
                #api-learner-panel .progress-bar { height: 8px; background: #eee; border-radius: 4px; overflow: hidden; margin-bottom: 10px; }
                #api-learner-panel #learner-progress-inner { height: 100%; width: 0%; background: #4caf50; transition: width 0.3s ease; }
                #api-learner-panel .log-container { max-height: 120px; overflow-y: auto; background: #fafafa; padding: 8px; border: 1px solid #eee; border-radius: 4px; font-size: 11px; line-height: 1.4; }
                #api-learner-panel .log-entry.error { color: #f44336; }
                #api-learner-panel .log-entry.success { color: #4caf50; }
                #api-learner-panel .log-entry.warn { color: #ff9800; }
                #api-learner-panel .footer { padding: 10px 15px; border-top: 1px solid #ddd; text-align: right; }
                #api-learner-panel button { padding: 6px 10px; border: none; border-radius: 4px; cursor: pointer; margin-left: 8px; font-size: 12px; }
                #api-learner-panel button#toggle-learning-btn { 
                    background: #2196f3; 
                    color: white; 
                    transition: background-color 0.3s ease;
                }
                #api-learner-panel button#toggle-learning-btn[data-state="running"] { 
                    background: #f44336; 
                }
                #api-learner-panel button:disabled { background: #ccc; }
                #api-learner-panel .feature-note { font-size: 11px; color: #666; margin-top: 8px; text-align: center; }
            `;
            const styleSheet = document.createElement("style");
            styleSheet.type = "text/css";
            styleSheet.innerText = styles;
            document.head.appendChild(styleSheet);
        },
        exportLogs: () => {
            if (UI.logs.length === 0) {
                alert('没有可导出的调试日志。');
                return;
            }
            const blob = new Blob([UI.logs.join('\r\n')], { type: 'text/plain;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `api_learner_debug_log_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }
    };

    // --- 学习策略模式 (v2.0优化) ---
    const LearningStrategies = {
        // 策略执行的辅助函数,封装重复逻辑
        async _executeSteps(context, steps, delay) {
            for (const targetTime of steps) {
                // 检查停止请求
                if (Learner.stopRequested) {
                    EventBus.publish('log', { message: '⏹️ 收到停止请求,中断策略执行', type: 'warn' });
                    return false;
                }
                
                await new Promise(resolve => setTimeout(resolve, delay));
                const success = await API.reportProgress(context.playInfo, targetTime);
                if (!success) return false;
                
                context.currentTime = targetTime;
                const progress = Math.floor((context.currentTime / context.duration) * 100);
                EventBus.publish('log', { message: `[策略执行] 进度: ${progress}%`, type: 'info' });
            }
            return true;
        },

        // 慢启动策略:分3步到50%,然后快速完成
        async slow_start(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[慢启动策略] 开始执行', type: 'info' });
            
            const steps = [0.2, 0.35, 0.5].map(p => Math.floor(duration * p));
            const success = await this._executeSteps(context, steps, 8000);
            
            if (success && !Learner.stopRequested) {
                // 最后冲刺到完成
                await new Promise(resolve => setTimeout(resolve, 5000));
                context.currentTime = duration - 10;
                return await API.reportProgress(context.playInfo, context.currentTime);
            }
            return success;
        },

        // 渐进式策略:分5步完成
        async progressive(context) {
            const { duration, currentTime } = context;
            EventBus.publish('log', { message: '[渐进式策略] 开始执行', type: 'info' });
            
            const remaining = duration - currentTime;
            const stepSize = Math.floor(remaining / 5);
            const steps = [];
            
            for (let i = 1; i <= 5; i++) {
                const nextTime = Math.min(currentTime + (stepSize * i), duration - 10);
                steps.push(nextTime);
            }
            
            return await this._executeSteps(context, steps, 6000);
        },

        // 快速完成策略:直接跳到95%然后完成
        async fast_finish(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[快速完成策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            const target95 = Math.floor(duration * 0.95);
            await new Promise(resolve => setTimeout(resolve, 3000));
            let success = await API.reportProgress(context.playInfo, target95);
            
            if (success && !Learner.stopRequested) {
                await new Promise(resolve => setTimeout(resolve, 5000));
                context.currentTime = duration - 10;
                success = await API.reportProgress(context.playInfo, context.currentTime);
            }
            return success;
        },

        // 最后冲刺策略:直接完成
        async final_push(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[最后冲刺策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            await new Promise(resolve => setTimeout(resolve, 3000));
            context.currentTime = duration - 10;
            return await API.reportProgress(context.playInfo, context.currentTime);
        },

        // 超快速策略:直接跳到结束
        async ultra_fast(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[超快速策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            await new Promise(resolve => setTimeout(resolve, 2000));
            context.currentTime = duration - 5;
            return await API.reportProgress(context.playInfo, context.currentTime);
        },

        // 策略选择器
        selectStrategy(currentProgress) {
            if (Settings.get('ULTRA_FAST_MODE')) {
                return 'ultra_fast';
            } else if (currentProgress < 10) {
                return 'slow_start';
            } else if (currentProgress < 50) {
                return 'progressive';
            } else if (currentProgress < 80) {
                return 'fast_finish';
            } else {
                return 'final_push';
            }
        }
    };

    // --- 工具函数 ---
    const Utils = {
        formatTime: function(seconds) {
            if (!seconds || seconds < 0) return '00:00:00';
            
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor((seconds % 3600) / 60);
            const secs = Math.floor(seconds % 60);
            
            return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        }
    };

    // --- API 核心(优化版) ---
    const API = {
        _baseUrl: 'https://cela.e-celap.cn',
        _videoApiBaseUrl: 'https://zpyapi.shsets.com',
        abortController: null, // AbortController 支持
        
        // 动态获取基础URL
        getBaseUrl: function() {
            if (CONFIG.PUDONG_MODE && CONFIG.PUDONG_API_BASE) {
                return CONFIG.PUDONG_API_BASE;
            }
            return this._baseUrl;
        },
        
        // 通用API端点尝试策略 (根据审查报告建议优化)
        async _tryApiEndpoints(apiCalls, successMessage, failureMessage) {
            for (let i = 0; i < apiCalls.length; i++) {
                // 检查是否被中止
                if (this.abortController && this.abortController.signal.aborted) {
                    throw new DOMException('Aborted', 'AbortError');
                }
                
                try {
                    const result = await apiCalls[i]();
                    if (this._isSuccessResponse(result)) {
                        EventBus.publish('log', { message: `${successMessage} (方法${i+1})`, type: 'success' });
                        return result;
                    }
                    EventBus.publish('log', { message: `[API Strategy] 方法${i+1}失败: ${result?.message || 'unknown error'}`, type: 'debug' });
                } catch (error) {
                    EventBus.publish('log', { message: `[API Strategy] 方法${i+1}异常: ${error.message}`, type: 'debug' });
                    if (error.name === 'AbortError') {
                        throw error; // 重新抛出中止错误
                    }
                }
            }
            EventBus.publish('log', { message: failureMessage, type: 'warn' });
            return null;
        },

        // 统一的成功响应判断逻辑
        _isSuccessResponse(result) {
            return result && (
                result.success === true || 
                result.code === 200 || 
                result.state === 20000 ||
                result.status === 'success' ||
                (result.code >= 200 && result.code < 300)
            );
        },
        
        _request: async function(options) {
            return new Promise((resolve, reject) => {
                // 检查是否被中止
                if (this.abortController && this.abortController.signal.aborted) {
                    return reject(new DOMException('Aborted', 'AbortError'));
                }
                
                // 提取Cookie和其他认证信息
                const cookies = document.cookie;
                const token = this._extractToken();
                
                // 构建请求头 - 根据数据类型设置Content-Type
                const headers = {
                    'Accept': 'application/json, text/plain, */*',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'X-Requested-With': 'XMLHttpRequest',
                    'Referer': window.location.href,
                    'Origin': this.getBaseUrl(),
                    'Cookie': cookies,
                    ...options.headers
                };
                
                // 根据数据类型设置Content-Type
                if (options.data instanceof FormData) {
                    // FormData会自动设置Content-Type,包括boundary
                    // 不要手动设置Content-Type
                } else if (typeof options.data === 'string' && options.data.includes('=')) {
                    // URL编码的表单数据
                    headers['Content-Type'] = 'application/x-www-form-urlencoded';
                } else {
                    // JSON数据
                    headers['Content-Type'] = 'application/json';
                }
                
                // 如果有token,添加到请求头
                if (token) {
                    headers['Authorization'] = `Bearer ${token}`;
                    headers['X-Auth-Token'] = token;
                }
                
                // 精简日志输出
                if (CONFIG.DEBUG_MODE) {
                    UI.log(`[API] ${options.method || 'GET'} ${options.url}`);
                }
                
                const req = GM_xmlhttpRequest({
                    method: options.method || 'GET',
                    url: options.url,
                    headers: headers,
                    data: options.data,
                    timeout: 30000,
                    onload: function(response) {
                        if (CONFIG.DEBUG_MODE) {
                            UI.log(`[API] ${response.status} ${response.responseText?.substring(0, 100)}...`);
                        }
                        
                        try {
                            if (response.responseText && response.responseText.trim()) {
                                const data = JSON.parse(response.responseText);
                                resolve(data);
                            } else {
                                resolve({ status: response.status, message: 'Empty response' });
                            }
                        } catch (parseError) {
                            UI.log(`❌ JSON解析失败: ${parseError.message}`, 'error');
                            resolve({ error: 'JSON解析失败', raw: response.responseText });
                        }
                    },
                    onerror: function(error) {
                        UI.log(`❌ 请求失败: ${error.message}`, 'error');
                        reject(error);
                    },
                    ontimeout: function() {
                        UI.log(`❌ 请求超时`, 'error');
                        reject(new Error('请求超时'));
                    }
                });
                
                // 支持AbortController
                if (this.abortController) {
                    this.abortController.signal.addEventListener('abort', () => {
                        if (req.abort) {
                            req.abort();
                        }
                        reject(new DOMException('Aborted', 'AbortError'));
                    });
                }
            });
        },
        
        _extractToken: function() {
            // 尝试从多个位置提取认证token
            const sources = [
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN),
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN),
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.ACCESS_TOKEN),
                () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN),
                () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN),
                () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'),
                () => window.token,
                () => window.authToken,
                () => {
                    const match = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.TOKEN);
                    return match ? match[1] : null;
                }
            ];
            
            for (const source of sources) {
                try {
                    const token = source();
                    if (token && token.length > 10) {
                        UI.log(`[Token] 找到认证token: ${token.substring(0, 20)}...`, 'debug');
                        return token;
                    }
                } catch (e) {
                    // 忽略提取错误
                }
            }
            
            UI.log('[Token] 未找到认证token', 'debug');
            return null;
        },

        /**
         * 进度上报 - 增强版,根据深度分析报告优化
         * 支持真实API优先,智能降级到模拟模式
         */
        reportProgress: async function(playInfo, currentTime) {
            try {
                EventBus.publish('log', { message: `[进度上报] 课程: ${playInfo.courseId}, 当前时间: ${currentTime}秒`, type: 'debug' });
                
                const isMockData = playInfo.videoId && playInfo.videoId.startsWith('mock_');
                const progressPercent = Math.round((currentTime / playInfo.duration) * 100);
                
                if (isMockData) {
                    EventBus.publish('log', { message: `[进度上报] 检测到模拟数据,将尝试真实API后再模拟`, type: 'debug' });
                }
                
                // 构建真实API调用方法(即使是模拟数据也要尝试)
                const reportMethods = [
                    // 方法1: 脉冲式进度上报(最接近真实用户行为)
                    () => this.pulseSaveRecord(playInfo, currentTime),
                    
                    // 方法2: 课程播放进度上报API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.REPORT_PROGRESS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            locationSiteId: playInfo.coursewareId,
                            videoId: isMockData ? playInfo.courseId : playInfo.videoId, // 模拟数据时使用courseId
                            currentTime: currentTime,
                            watchPoint: this.secondsToTimeString(currentTime),
                            duration: playInfo.duration,
                            progress: progressPercent,
                            _t: Date.now()
                        }),
                        headers: {
                            'Content-Type': 'application/json',
                            'Accept': 'application/json, text/plain, */*'
                        }
                    }),
                    
                    // 方法3: 视频播放进度更新API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.UPDATE_PROGRESS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            coursewareId: playInfo.coursewareId,
                            progress: progressPercent,
                            currentTime: currentTime,
                            lastWatchPoint: this.secondsToTimeString(currentTime),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法4: 学习记录保存API(适合模拟数据)
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SAVE_STUDY_RECORD}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            videoId: isMockData ? playInfo.courseId : playInfo.videoId,
                            studyTime: currentTime,
                            totalTime: playInfo.duration,
                            finishRate: progressPercent,
                            watchPoint: this.secondsToTimeString(currentTime),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                // 如果不是模拟数据,添加视频服务API
                if (!isMockData) {
                    reportMethods.push(
                        () => this._request({
                            method: 'POST',
                            url: `${this._videoApiBaseUrl}${CONSTANTS.API_ENDPOINTS.VIDEO_PROGRESS}`,
                            data: JSON.stringify({
                                videoId: playInfo.videoId,
                                currentTime: currentTime,
                                duration: playInfo.duration,
                                progress: progressPercent,
                                _t: Date.now()
                            }),
                            headers: { 'Content-Type': 'application/json' }
                        })
                    );
                }
                
                // 尝试真实API调用
                const result = await this._tryApiEndpoints(
                    reportMethods,
                    `[进度上报] 成功 (${progressPercent}%)`,
                    `[进度上报] 所有API方法失败`
                );
                
                if (result) {
                    // 真实API成功
                    if (isMockData) {
                        EventBus.publish('log', { message: `[进度上报] 模拟数据成功调用真实API`, type: 'success' });
                    }
                    return result;
                }
                
                // 所有真实API都失败,使用兜底策略
                if (CONFIG.FALLBACK_MODE) {
                    if (isMockData) {
                        EventBus.publish('log', { message: `[模拟上报] 模拟进度上报: ${currentTime}秒 (${progressPercent}%)`, type: 'info' });
                    } else {
                        EventBus.publish('log', { message: `[进度上报] 启用兜底模式: ${currentTime}秒 (${progressPercent}%)`, type: 'warn' });
                    }
                    
                    // 模拟网络延迟
                    await new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1000));
                    return { 
                        code: 200, 
                        success: true,
                        msg: isMockData ? 'mock success' : 'fallback success',
                        data: { currentTime, progress: progressPercent }
                    };
                } else {
                    throw new Error('所有进度上报方法失败且兜底模式已禁用');
                }
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    EventBus.publish('log', { message: `[进度上报] 已中止`, type: 'warn' });
                    throw error;
                }
                EventBus.publish('log', { message: `[进度上报] 发生错误: ${error.message}`, type: 'error' });
                
                // 最终兜底
                if (CONFIG.FALLBACK_MODE) {
                    return { code: 200, success: true, msg: 'error fallback success' };
                } else {
                    throw error;
                }
            }
        },

        // 使用优化后的API策略重构学习记录创建
        _createStudyRecord: async function(courseId) {
            try {
                UI.log(`[学习记录] 创建课程 ${courseId} 的学习记录`, 'debug');
                
                const createMethods = [
                    // 方法1: 开始学习记录
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.START_STUDY}`,
                        data: new URLSearchParams({
                            courseId: courseId,
                            startTime: new Date().toISOString(),
                            _t: Date.now()
                        }).toString(),
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                    }),
                    
                    // 方法2: 创建学习会话
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.CREATE_SESSION}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            sessionStart: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法3: 学习记录初始化
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.INIT_STUDY_RECORD}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            initTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                const result = await this._tryApiEndpoints(
                    createMethods,
                    `[学习记录] 创建成功`,
                    `[学习记录] 所有创建方法失败,将跳过记录创建`
                );
                
                return !!result;
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[学习记录] 创建异常: ${error.message}`, 'warn');
                return false;
            }
        },

        // 使用优化后的API策略重构学习记录完成
        finishStudyRecord: async function(playInfo, finalTime) {
            try {
                UI.log(`[学习记录] 完成课程 ${playInfo.courseId} 的学习记录`, 'debug');
                
                const finishMethods = [
                    // 方法1: 完成学习API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.FINISH_STUDY}`,
                        data: new URLSearchParams({
                            courseId: playInfo.courseId,
                            coursewareId: playInfo.coursewareId,
                            studyTime: finalTime,
                            finishTime: new Date().toISOString(),
                            isFinished: true,
                            _t: Date.now()
                        }).toString(),
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                    }),
                    
                    // 方法2: 完成课程学习
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.COMPLETE_LEARNING}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            finalTime: finalTime,
                            duration: playInfo.duration,
                            completionRate: 100,
                            endTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法3: 保存完成状态
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SAVE_COMPLETION_STATUS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            status: 'completed',
                            completedAt: new Date().toISOString(),
                            totalTime: finalTime,
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                const result = await this._tryApiEndpoints(
                    finishMethods,
                    `[学习记录] 完成成功`,
                    `[学习记录] 所有完成方法失败,学习记录可能未正确保存`
                );
                
                return !!result;
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[学习记录] 完成异常: ${error.message}`, 'warn');
                return false;
            }
        },

        // 使用优化后的API策略重构课程完成
        completeCourse: async function(courseInfo) {
            try {
                const courseId = courseInfo.id || courseInfo.courseId;
                UI.log(`[课程完成] 开始完成课程: ${courseId}`, 'debug');

                const completionMethods = [
                    // 方法1: 课程学习完成API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.COMPLETE_COURSE}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            completionTime: new Date().toISOString(),
                            studyDuration: courseInfo.durationStr || '00:30:00',
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法2: 学习进度完成API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.FINISH_LEARNING}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            status: 'completed',
                            progress: 100,
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法3: 课程状态更新API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.UPDATE_STATUS}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            status: 'finished',
                            finishTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法4: 学习记录提交API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SUBMIT_LEARNING_RECORD}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            learningTime: courseInfo.durationStr || '00:30:00',
                            isCompleted: true,
                            submitTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法5: 课程结业API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GRADUATE}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            graduateTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];

                const result = await this._tryApiEndpoints(
                    completionMethods,
                    `[课程完成] 完成成功`,
                    `[课程完成] 所有方法失败,课程可能已通过其他方式完成`
                );

                return !!result;

            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[课程完成] 发生错误: ${error.message}`, 'error');
                return false;
            }
        },

        // 剩余的API方法(使用常量优化)
        
        getCourseListFromSpecialDetail: async () => {
            try {
                UI.log('检测到专栏详情页面,尝试获取课程列表...', 'info');
                
                const urlParams = new URLSearchParams(window.location.hash.split('?')[1]);
                const channelId = urlParams.get('id');
                
                if (!channelId) {
                    UI.log('未找到专栏ID', 'error');
                    return [];
                }
                
                UI.log(`专栏ID: ${channelId}`, 'debug');
                
                const url = `${CONSTANTS.API_ENDPOINTS.GET_PACK_BY_ID}?id=${channelId}&_t=${Date.now()}`;
                const response = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${url}`
                });
                
                if (!response.success || !response.data) {
                    UI.log('专栏API返回数据异常', 'error');
                    return [];
                }
                
                const channelData = response.data;
                UI.log(`专栏标题: ${channelData.title}`, 'info');
                
                const courseList = [];
                if (channelData.pdChannelUnitList) {
                    for (const unit of channelData.pdChannelUnitList) {
                        UI.log(`单元: ${unit.unitName} (${unit.totalPeriod}学时)`, 'debug');
                        
                        if (unit.subList) {
                            for (const course of unit.subList) {
                                if (course.typeValue === 'course') {
                                    courseList.push({
                                        id: course.businessId,
                                        courseId: course.businessId,
                                        dsUnitId: `unit_${unit.order}_${course.order}`,
                                        title: course.title,
                                        courseName: course.title,
                                        teacher: course.teacher,
                                        teacherJob: course.teacherjob,
                                        durationStr: course.duration,
                                        period: course.period,
                                        unit: unit.unitName,
                                        category: course.categoryText,
                                        publishTime: course.publishTime,
                                        order: course.order,
                                        unitOrder: unit.order,
                                        status: 'not_started'
                                    });
                                }
                            }
                        }
                    }
                }
                
                UI.log(`[专栏课程] 成功获取 ${courseList.length} 门课程`, 'info');
                return courseList;
                
            } catch (error) {
                UI.log(`获取专栏课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        getCourseListFromChannel: async function(channelId) {
            try {
                UI.log(`正在从频道API获取课程列表 (ID: ${channelId})...`, 'info');
                
                const apiEndpoints = [
                    `${CONSTANTS.API_ENDPOINTS.GET_PACK_BY_ID}?id=${channelId}&_t=${Date.now()}`,
                    `/api/nc/channel/detail?id=${channelId}&_t=${Date.now()}`,
                    `/inc/nc/course/list?channelId=${channelId}&_t=${Date.now()}`,
                    `${CONSTANTS.API_ENDPOINTS.GET_COURSE_LIST}?channelId=${channelId}&_t=${Date.now()}`
                ];
                
                for (const endpoint of apiEndpoints) {
                    try {
                        UI.log(`尝试API端点: ${endpoint}`, 'debug');
                        const response = await API._request({
                            method: 'GET',
                            url: `${API.getBaseUrl()}${endpoint}`
                        });
                        
                        if (response && response.success && response.data) {
                            const courseList = [];
                            const data = response.data;
                            
                            if (data.pdChannelUnitList) {
                                for (const unit of data.pdChannelUnitList) {
                                    UI.log(`单元: ${unit.unitName} (${unit.totalPeriod}学时)`, 'debug');
                                    
                                    if (unit.subList) {
                                        for (const course of unit.subList) {
                                            if (course.typeValue === 'course') {
                                                courseList.push({
                                                    id: course.businessId,
                                                    courseId: course.businessId,
                                                    dsUnitId: `unit_${unit.order}_${course.order}`,
                                                    title: course.title,
                                                    courseName: course.title,
                                                    teacher: course.teacher,
                                                    teacherJob: course.teacherjob,
                                                    durationStr: course.duration,
                                                    period: course.period,
                                                    unit: unit.unitName,
                                                    category: course.categoryText,
                                                    publishTime: course.publishTime,
                                                    order: course.order,
                                                    unitOrder: unit.order,
                                                    status: 'not_started'
                                                });
                                            }
                                        }
                                    }
                                }
                            } else {
                                let courses = [];
                                if (data.courseList) {
                                    courses = data.courseList;
                                } else if (data.courses) {
                                    courses = data.courses;
                                } else if (data.list) {
                                    courses = data.list;
                                } else if (Array.isArray(data)) {
                                    courses = data;
                                }
                                
                                courses.forEach(course => {
                                    courseList.push({
                                        courseId: course.id || course.courseId,
                                        courseName: course.name || course.title || course.courseName,
                                        durationStr: course.duration || course.durationStr || '00:30:00',
                                        status: course.status || 'not_started'
                                    });
                                });
                            }
                            
                            if (courseList.length > 0) {
                                UI.log(`✅ 从频道API获取到 ${courseList.length} 门课程`, 'info');
                                return courseList;
                            }
                        }
                    } catch (error) {
                        UI.log(`API端点 ${endpoint} 失败: ${error.message}`, 'debug');
                    }
                }
                
                UI.log('❌ 所有频道API端点都失败了', 'warn');
                return [];
            } catch (error) {
                UI.log(`❌ 获取频道课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        getCourseList: async () => {
            try {
                UI.log('正在获取课程列表...', 'info');
                
                const waitForVueApp = async () => {
                    let attempts = 0;
                    const maxAttempts = 15;
                    
                    while (attempts < maxAttempts) {
                        const app = document.querySelector(CONSTANTS.SELECTORS.APP);
                        if (app && (window.Vue || app.__vue__ || app._vnode)) {
                            UI.log('✅ 检测到Vue.js应用已加载', 'debug');
                            return true;
                        }
                        
                        const hasContent = document.querySelectorAll('.el-card, [class*="course"], [class*="item"], [class*="card"]').length > 0;
                        if (hasContent) {
                            UI.log('✅ 检测到动态内容已加载', 'debug');
                            return true;
                        }
                        
                        UI.log(`⏳ 等待Vue.js应用加载... (${attempts + 1}/${maxAttempts})`, 'debug');
                        await new Promise(resolve => setTimeout(resolve, 1000));
                        attempts++;
                    }
                    
                    UI.log('⚠️ Vue.js应用加载超时,继续尝试获取课程', 'warn');
                    return false;
                };
                
                await waitForVueApp();
                
                const currentUrl = window.location.href;
                UI.log(`当前页面URL: ${currentUrl}`, 'debug');
                
                if (currentUrl.includes('/specialdetail')) {
                    UI.log('检测到专栏详情页面,尝试从API获取课程列表...', 'info');
                    return await API.getCourseListFromSpecialDetail();
                }
                
                if (currentUrl.includes('channelDetail')) {
                    UI.log('检测到频道详情页面,尝试从API获取课程列表...', 'info');
                    
                    let channelId = null;
                    try {
                        const hash = window.location.hash;
                        if (hash.includes('?')) {
                            const urlParams = new URLSearchParams(hash.split('?')[1]);
                            channelId = urlParams.get('id');
                        }
                        
                        if (!channelId && currentUrl.includes('?')) {
                            const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
                            channelId = urlParams.get('id');
                        }
                        
                        if (!channelId) {
                            const match = currentUrl.match(/[?&]id=([^&]+)/);
                            if (match) {
                                channelId = match[1];
                            }
                        }
                    } catch (error) {
                        UI.log(`解析频道ID失败: ${error.message}`, 'debug');
                    }
                    
                    if (channelId) {
                        UI.log(`频道ID: ${channelId}`, 'debug');
                        return await API.getCourseListFromChannel(channelId);
                    } else {
                        UI.log('❌ 无法从URL中提取频道ID', 'warn');
                    }
                }
                
                let courseList = [];
                let courseElements = [];
                
                await new Promise(resolve => setTimeout(resolve, 3000));
                
                UI.log(`🔍 页面调试信息:`, 'debug');
                UI.log(`- 页面标题: ${document.title}`, 'debug');
                UI.log(`- ${CONSTANTS.SELECTORS.APP}元素: ${document.querySelector(CONSTANTS.SELECTORS.APP) ? '存在' : '不存在'}`, 'debug');
                UI.log(`- Vue实例: ${window.Vue ? '存在' : '不存在'}`, 'debug');
                UI.log(`- 所有元素数量: ${document.querySelectorAll('*').length}`, 'debug');
                
                for (const selector of CONSTANTS.COURSE_SELECTORS) {
                    const elements = document.querySelectorAll(selector);
                    UI.log(`🔍 选择器 "${selector}": ${elements.length} 个元素`, 'debug');
                    if (elements.length > 0) {
                        courseElements = elements;
                        UI.log(`📋 使用选择器 "${selector}" 找到 ${elements.length} 个课程元素`, 'info');
                        break;
                    }
                }
                
                if (courseElements.length === 0) {
                    for (const selector of CONSTANTS.FALLBACK_SELECTORS) {
                        const elements = document.querySelectorAll(selector);
                        if (elements.length > 0) {
                            courseElements = elements;
                            UI.log(`📋 使用备用选择器 "${selector}" 找到 ${elements.length} 个元素`, 'info');
                            break;
                        }
                    }
                }
                
                UI.log(`[DOM解析] 找到 ${courseElements.length} 个课程元素`, 'debug');
                
                courseElements.forEach((el, index) => {
                    const courseData = {
                        courseId: el.getAttribute('data-course-id') || 
                                 el.getAttribute('data-id') || 
                                 el.querySelector('[data-course-id]')?.getAttribute('data-course-id') ||
                                 `course_${index}`,
                        dsUnitId: el.getAttribute('data-unit-id') || 
                                 el.getAttribute('data-dsunit') ||
                                 el.querySelector('[data-unit-id]')?.getAttribute('data-unit-id') ||
                                 `unit_${index}`,
                        courseName: el.getAttribute('title') || 
                                   el.querySelector('.course-title, .lesson-title, h3, h4')?.textContent?.trim() ||
                                   el.textContent?.trim()?.split('\n')[0] ||
                                   `课程${index + 1}`,
                        durationStr: el.querySelector('.duration, .time, [class*="time"]')?.textContent?.trim() || '00:30:00',
                        status: el.getAttribute('data-status') || 'not_started'
                    };
                    
                    if (courseData.courseName && courseData.courseName.length > 5) {
                        courseList.push(courseData);
                    }
                });
                
                if (courseList.length === 0) {
                    try {
                        const apiUrl = `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_COURSE_LIST}`;
                        const apiResponse = await API._request({
                            method: 'GET',
                            url: apiUrl + '?' + new URLSearchParams({
                                _t: Date.now(),
                                page: 1,
                                size: 50
                            }).toString()
                        });
                        
                        if (apiResponse.success && apiResponse.data) {
                            const apiCourses = Array.isArray(apiResponse.data) ? apiResponse.data : 
                                              apiResponse.data.list || apiResponse.data.records || [];
                            courseList = apiCourses.map((course, index) => ({
                                courseId: course.id || course.courseId || `api_course_${index}`,
                                dsUnitId: course.dsUnitId || course.unitId || `api_unit_${index}`,
                                courseName: course.name || course.title || course.courseName || `API课程${index + 1}`,
                                durationStr: course.duration || course.timeLength || '00:30:00',
                                status: course.status || 'not_started'
                            }));
                        }
                    } catch (apiError) {
                        UI.log(`[API获取课程] 失败: ${apiError.message}`, 'debug');
                    }
                }
                
                if (courseList.length === 0) {
                    const videoElements = document.querySelectorAll(CONSTANTS.VIDEO_SELECTORS.join(', '));
                    UI.log(`[视频元素分析] 找到 ${videoElements.length} 个视频元素`, 'debug');
                    
                    videoElements.forEach((el, index) => {
                        const courseData = {
                            courseId: el.getAttribute('data-course-id') || `video_course_${index}`,
                            dsUnitId: el.getAttribute('data-unit-id') || `video_unit_${index}`,
                            courseName: document.title || `视频课程${index + 1}`,
                            durationStr: el.getAttribute('duration') || '00:30:00',
                            status: 'not_started',
                            videoElement: el
                        };
                        courseList.push(courseData);
                    });
                }
                
                const uniqueCourses = courseList.filter((course, index, self) => 
                    index === self.findIndex(c => c.courseId === course.courseId)
                );
                
                UI.log(`[课程列表] 获取到 ${uniqueCourses.length} 门课程`, 'info');
                uniqueCourses.forEach(course => {
                    UI.log(`- ${course.courseName} (${course.courseId})`, 'debug');
                });
                
                return uniqueCourses;
                
            } catch (error) {
                UI.log(`获取课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        /**
         * 获取课程播放信息 - 增强版,支持多种数据源和智能降级
         * 根据深度分析报告优化:处理API空数据、增强videoId提取、智能时长解析
         */
        getPlayInfo: async (courseId, dsUnitId, courseDuration) => {
            try {
                EventBus.publish('log', { message: `[getPlayInfo] 开始获取课程 ${courseId} 的播放信息`, type: 'debug' });
                
                // 策略1: 尝试从播放趋势API获取数据
                let playTrendData = null;
                try {
                    const playTrendParams = new URLSearchParams({
                        courseId: courseId,
                        _t: Date.now()
                    });
                    playTrendData = await API._request({
                        method: 'GET',
                        url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_PLAY_TREND}?${playTrendParams.toString()}`
                    });
                    EventBus.publish('log', { message: `[getPlayTrend] 响应数据: ${JSON.stringify(playTrendData)}`, type: 'debug' });
                } catch (error) {
                    EventBus.publish('log', { message: `[getPlayTrend] API调用失败: ${error.message}`, type: 'warn' });
                }

                let duration = 0;
                let lastLearnedTime = 0;
                let videoId = null;
                let coursewareId = null;
                let dataSource = 'unknown';

                // 检查API是否返回有效数据(根据深度分析报告的建议)
                if (playTrendData?.success && playTrendData?.data && 
                    Object.keys(playTrendData.data).length > 0 && playTrendData.data.locationSite) {
                    
                    dataSource = 'api';
                    const trendData = playTrendData.data;
                    const locationSite = trendData.locationSite;
                    
                    // 提取时长信息
                    duration = locationSite.sumDurationLong || locationSite.duration || 0;
                    
                    // 提取学习进度
                    if (locationSite.lastWatchPoint) {
                        lastLearnedTime = API.parseTimeToSeconds(locationSite.lastWatchPoint);
                    } else if (locationSite.lastLearnedTime) {
                        lastLearnedTime = locationSite.lastLearnedTime;
                    }
                    
                    coursewareId = locationSite.id;
                    
                    // 增强的videoId提取逻辑(根据学习记录API修复报告)
                    const videoIdSources = [
                        // 方法1: 从fileUrl中提取
                        () => {
                            if (locationSite.fileUrl) {
                                try {
                                    const fileUrlData = JSON.parse(locationSite.fileUrl);
                                    if (fileUrlData.file?.[0]?.id) {
                                        return fileUrlData.file[0].id;
                                    }
                                    if (fileUrlData.videoId) {
                                        return fileUrlData.videoId;
                                    }
                                } catch (e) {
                                    EventBus.publish('log', { message: `[videoId提取] fileUrl解析失败: ${e.message}`, type: 'debug' });
                                }
                            }
                            return null;
                        },
                        
                        // 方法2: 从fileAdditionUrl中提取
                        () => {
                            if (locationSite.fileAdditionUrl) {
                                try {
                                    const additionData = JSON.parse(locationSite.fileAdditionUrl);
                                    if (additionData.videoId) {
                                        return additionData.videoId;
                                    }
                                } catch (e) {
                                    EventBus.publish('log', { message: `[videoId提取] fileAdditionUrl解析失败: ${e.message}`, type: 'debug' });
                                }
                            }
                            return null;
                        },
                        
                        // 方法3: 从playTree中查找
                        () => {
                            if (trendData.playTree?.children) {
                                for (const child of trendData.playTree.children) {
                                    if (child.rTypeValue === 'video' && child.id) {
                                        coursewareId = child.id;
                                        return child.videoId || child.id;
                                    }
                                }
                            }
                            return null;
                        }
                    ];
                    
                    // 尝试各种方法提取videoId
                    for (const source of videoIdSources) {
                        try {
                            const extractedId = source();
                            if (extractedId) {
                                videoId = extractedId;
                                EventBus.publish('log', { message: `[videoId提取] 成功提取: ${videoId}`, type: 'debug' });
                                break;
                            }
                        } catch (e) {
                            EventBus.publish('log', { message: `[videoId提取] 方法失败: ${e.message}`, type: 'debug' });
                        }
                    }
                    
                    EventBus.publish('log', { message: `[getPlayInfo] 使用API数据源`, type: 'info' });
                } else {
                    // 策略2: API返回空数据,使用课程列表数据(根据深度分析报告)
                    dataSource = 'courselist';
                    EventBus.publish('log', { message: `[getPlayInfo] API返回空数据,使用课程列表数据源`, type: 'warn' });
                }

                // 时长处理:优先使用API数据,否则解析课程列表时长
                if (duration === 0 && courseDuration) {
                    duration = API.parseDuration(courseDuration);
                    EventBus.publish('log', { message: `[getPlayInfo] 从课程列表解析时长: ${courseDuration} → ${duration}秒`, type: 'debug' });
                }

                // 兜底时长
                if (duration === 0) {
                    duration = CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                    EventBus.publish('log', { message: `[getPlayInfo] 使用默认时长: ${duration}秒`, type: 'debug' });
                }

                // 尝试通过课件详情API获取videoId
                if (!videoId && coursewareId) {
                    try {
                        const coursewareDetail = await API._request({
                            method: 'GET',
                            url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_COURSEWARE_DETAIL}?coursewareId=${coursewareId}&_t=${Date.now()}`
                        });
                        
                        if (coursewareDetail?.success && coursewareDetail?.data?.videoId) {
                            videoId = coursewareDetail.data.videoId;
                            EventBus.publish('log', { message: `[getPlayInfo] 从课件详情获取videoId: ${videoId}`, type: 'debug' });
                        }
                    } catch (e) {
                        EventBus.publish('log', { message: `[getPlayInfo] 查询课件详情失败: ${e.message}`, type: 'debug' });
                    }
                }

                // 生成模拟videoId(但会尝试真实API)
                if (!videoId) {
                    videoId = `mock_video_${courseId}`;
                    EventBus.publish('log', { message: `[getPlayInfo] 生成模拟videoId: ${videoId}`, type: 'debug' });
                }

                const playInfo = {
                    courseId: courseId,
                    coursewareId: coursewareId || dsUnitId,
                    videoId: videoId,
                    duration: duration,
                    lastLearnedTime: lastLearnedTime,
                    playURL: videoId.startsWith('mock_') ? 'mock://play.url' : `https://zpyapi.shsets.com/player/get?videoId=${videoId}`,
                    dataSource: dataSource // 添加数据源标识
                };

                EventBus.publish('log', { message: `[getPlayInfo] 最终播放信息 (${dataSource}): ${JSON.stringify(playInfo)}`, type: 'debug' });
                return playInfo;

            } catch (error) {
                EventBus.publish('log', { message: `[getPlayInfo] 错误: ${error.message}`, type: 'error' });
                
                // 完全兜底方案
                const duration = courseDuration ? API.parseDuration(courseDuration) : CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                return {
                    courseId: courseId,
                    coursewareId: dsUnitId,
                    videoId: `mock_video_${courseId}`,
                    duration: duration,
                    lastLearnedTime: 0,
                    playURL: 'mock://play.url',
                    dataSource: 'fallback'
                };
            }
        },

        parseTimeToSeconds: (timeStr) => {
            try {
                const parts = timeStr.split(':').map(part => parseInt(part, 10));
                if (parts.length === 3) {
                    return parts[0] * 3600 + parts[1] * 60 + parts[2];
                }
                return 0;
            } catch (e) {
                return 0;
            }
        },

        parseDuration: (durationStr) => {
            if (!durationStr || typeof durationStr !== 'string') return CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
            const timeParts = durationStr.split(':');
            if (timeParts.length === 3) {
                const hours = parseInt(timeParts[0]) || 0;
                const minutes = parseInt(timeParts[1]) || 0;
                const seconds = parseInt(timeParts[2]) || 0;
                return hours * 3600 + minutes * 60 + seconds;
            }
            return CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
        },

        pulseSaveRecord: async (playInfo, currentTime) => {
            const watchPoint = API.secondsToTimeString(currentTime);
            const payload = new URLSearchParams({
                courseId: playInfo.courseId,
                coursewareId: playInfo.coursewareId || playInfo.videoId,
                watchPoint: watchPoint,
                pulseTime: 10,
                pulseRate: 1
            }).toString();
            
            UI.log(`[脉冲上报] ${watchPoint} (${Math.round((currentTime / playInfo.duration) * 100)}%)`, 'info');
            
            return await API._request({
                method: 'POST',
                url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.PULSE_SAVE_RECORD}`,
                data: payload,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            });
        },

        secondsToTimeString: (seconds) => {
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor((seconds % 3600) / 60);
            const secs = seconds % 60;
            return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        },

        antiCheatCheck: async (courseId, userId) => {
            try {
                UI.log(`[防刷课检查] 课程ID: ${courseId}`, 'debug');
                
                const pushUrl = `${CONSTANTS.API_ENDPOINTS.PUSH_COURSE}?user_id=${userId}&course_id=${courseId}&_t=${Date.now()}`;
                const pushResponse = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${pushUrl}`
                });
                
                const checkUrl = `${CONSTANTS.API_ENDPOINTS.GET_COURSE_BY_USER}?user_id=${userId}&course_id=${courseId}&_t=${Date.now()}`;
                const checkResponse = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${checkUrl}`
                });
                
                UI.log(`[防刷课检查结果] Push: ${pushResponse?.message || '未知'}, Check: ${checkResponse?.message || '未知'}`, 'debug');
                
                return {
                    pushOk: pushResponse?.success === true,
                    checkOk: checkResponse?.success === true
                };
            } catch (error) {
                UI.log(`[防刷课检查失败] ${error.message}`, 'error');
                return { pushOk: false, checkOk: false };
            }
        },

        extractUserId: () => {
            try {
                const cookieMatch = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.USER_ID);
                if (cookieMatch) {
                    UI.log(`[用户ID提取] 从Cookie获取: ${cookieMatch[1]}`, 'debug');
                    return cookieMatch[1];
                }
                
                const userIdElement = document.querySelector('[data-user-id]');
                if (userIdElement) {
                    const userId = userIdElement.getAttribute('data-user-id');
                    UI.log(`[用户ID提取] 从DOM获取: ${userId}`, 'debug');
                    return userId;
                }
                
                const urlParams = new URLSearchParams(window.location.search);
                const userId = urlParams.get('user_id');
                if (userId) {
                    UI.log(`[用户ID提取] 从URL获取: ${userId}`, 'debug');
                    return userId;
                }
                
                const storedUserId = localStorage.getItem(CONSTANTS.STORAGE_KEYS.USER_ID) || localStorage.getItem(CONSTANTS.STORAGE_KEYS.USER_ID_ALT);
                if (storedUserId) {
                    UI.log(`[用户ID提取] 从localStorage获取: ${storedUserId}`, 'debug');
                    return storedUserId;
                }
                
                const pMatch = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.P_PARAM);
                if (pMatch) {
                    UI.log(`[用户ID提取] 从_p参数获取: ${pMatch[1]}`, 'debug');
                    return pMatch[1];
                }
                
                UI.log('[用户ID提取] 未找到用户ID', 'warn');
                return null;
            } catch (error) {
                UI.log(`[用户ID提取失败] ${error.message}`, 'error');
                return null;
            }
        },

        async checkCourseCompletion(courseId) {
            try {
                UI.log(`[完成度检查] 检查课程 ${courseId} 的完成状态`);
                
                const playTrend = await this._request({
                    url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_PLAY_TREND}?courseId=${courseId}&_t=${Date.now()}`,
                    method: 'GET'
                });
                
                if (playTrend && playTrend.success && playTrend.data) {
                    const { locationSite } = playTrend.data;
                    if (locationSite && locationSite.finishedRate !== undefined) {
                        const finishedRate = parseInt(locationSite.finishedRate);
                        UI.log(`[完成度检查] 课程完成度: ${finishedRate}%`);
                        
                        if (finishedRate >= CONFIG.COMPLETION_THRESHOLD) {
                            UI.log(`[完成度检查] 课程已完成 (${finishedRate}% >= ${CONFIG.COMPLETION_THRESHOLD}%)`, 'success');
                            return { isCompleted: true, finishedRate, method: 'playTrend' };
                        }
                        
                        return { isCompleted: false, finishedRate, method: 'playTrend' };
                    }
                }
                
                try {
                    const studyRecord = await this._request({
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_STUDY_RECORD}?courseId=${courseId}&_t=${Date.now()}`,
                        method: 'GET'
                    });
                    
                    if (studyRecord && studyRecord.success && studyRecord.data) {
                        const isFinished = studyRecord.data.isFinished === true || studyRecord.data.status === 'completed';
                        if (isFinished) {
                            UI.log(`[完成度检查] 学习记录显示课程已完成`, 'success');
                            return { isCompleted: true, finishedRate: 100, method: 'studyRecord' };
                        }
                    }
                } catch (error) {
                    UI.log(`[完成度检查] 学习记录API检查失败: ${error.message}`, 'warn');
                }
                
                return { isCompleted: false, finishedRate: 0, method: 'default' };
                
            } catch (error) {
                UI.log(`[完成度检查] 检查失败: ${error.message}`, 'error');
                return { isCompleted: false, finishedRate: 0, method: 'error' };
            }
        },

        /**
         * 智能学习课程 - 根据当前进度自动选择最佳学习策略
         * 
         * @param {Object} courseInfo - 课程信息对象
         * @param {string} courseInfo.courseId - 课程ID
         * @param {string} courseInfo.coursewareId - 课件ID
         * @param {string} courseInfo.videoId - 视频ID
         * @param {number} courseInfo.duration - 课程总时长(秒)
         * @param {number} courseInfo.lastLearnedTime - 上次学习时间点(秒)
         * @param {string} courseInfo.title - 课程标题
         * @returns {Promise<boolean>} 学习是否成功
         */
        async smartLearnCourse(courseInfo) {
            const { courseId, coursewareId, videoId, duration, lastLearnedTime } = courseInfo;
            const currentProgress = Math.floor((lastLearnedTime / duration) * 100);
            
            EventBus.publish('log', { message: `[智能学习] 课程: ${courseInfo.title || courseId}`, type: 'info' });
            EventBus.publish('log', { message: `[智能学习] 当前进度: ${currentProgress}% (${Utils.formatTime(lastLearnedTime)}/${Utils.formatTime(duration)})`, type: 'info' });
            
            let strategy = 'normal';
            
            if (currentProgress < 10) {
                strategy = 'slow_start';
                UI.log(`[智能学习] 策略: 慢启动 - 从头开始学习`);
            } else if (currentProgress < 50) {
                strategy = 'progressive';
                UI.log(`[智能学习] 策略: 渐进式 - 从${currentProgress}%继续`);
            } else if (currentProgress < 80) {
                strategy = 'fast_finish';
                UI.log(`[智能学习] 策略: 快速完成 - 直接跳跃到结束`);
            } else {
                strategy = 'final_push';
                UI.log(`[智能学习] 策略: 最后冲刺 - 完成剩余部分`);
            }
            
            await this._createStudyRecord(courseId);
            
            let currentTime = lastLearnedTime;
            let success = false;
            
            switch (strategy) {
                case 'slow_start':
                    const step1 = Math.floor(duration * 0.2);
                    const step2 = Math.floor(duration * 0.35);
                    const step3 = Math.floor(duration * 0.5);
                    
                    for (const targetTime of [step1, step2, step3]) {
                        if (Learner.stopRequested) {
                            UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                            return false;
                        }
                        
                        await new Promise(resolve => setTimeout(resolve, 8000));
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, targetTime);
                        if (!success) break;
                        currentTime = targetTime;
                        UI.log(`[慢启动] 进度: ${Math.floor((currentTime/duration)*100)}%`);
                    }
                    
                    if (success && !Learner.stopRequested) {
                        await new Promise(resolve => setTimeout(resolve, 5000));
                        currentTime = duration - 10;
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    }
                    break;
                    
                case 'progressive':
                    const remaining = duration - currentTime;
                    const stepSize = Math.floor(remaining / 5);
                    
                    for (let i = 1; i <= 5; i++) {
                        if (Learner.stopRequested) {
                            UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                            return false;
                        }
                        
                        await new Promise(resolve => setTimeout(resolve, 6000));
                        const nextTime = Math.min(currentTime + (stepSize * i), duration - 10);
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, nextTime);
                        if (!success) break;
                        currentTime = nextTime;
                        UI.log(`[渐进式] 步骤 ${i}/5: ${Math.floor((currentTime/duration)*100)}%`);
                    }
                    break;
                    
                case 'fast_finish':
                    if (Learner.stopRequested) {
                        UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                        return false;
                    }
                    
                    const target95 = Math.floor(duration * 0.95);
                    await new Promise(resolve => setTimeout(resolve, 3000));
                    success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, target95);
                    
                    if (success && !Learner.stopRequested) {
                        await new Promise(resolve => setTimeout(resolve, 5000));
                        currentTime = duration - 10;
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    }
                    break;
                    
                case 'final_push':
                    if (Learner.stopRequested) {
                        UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                        return false;
                    }
                    
                    await new Promise(resolve => setTimeout(resolve, 3000));
                    currentTime = duration - 10;
                    success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    break;
            }
            
            if (success) {
                UI.log(`✅ 智能学习完成: ${courseInfo.title || courseId}`, 'success');
                
                try {
                    await this.finishStudyRecord(courseInfo, currentTime);
                    await this.completeCourse(courseInfo);
                    UI.log(`✅ 学习记录已保存`, 'success');
                } catch (error) {
                    UI.log(`⚠️ 学习记录保存失败: ${error.message}`, 'warn');
                }
                
                return true;
            } else {
                UI.log(`❌ 智能学习失败`, 'error');
                return false;
            }
        }
    };

    // --- 主控制逻辑(增强版) ---
    const Learner = {
        isRunning: false,
        stopRequested: false,
        
        stop: function() {
            this.isRunning = false;
            this.stopRequested = true;
            
            // 使用AbortController真正中止所有正在进行的请求
            if (API.abortController) {
                API.abortController.abort();
                UI.log('🛑 正在中止所有网络请求...', 'info');
            }
            
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
            if (toggleBtn) {
                toggleBtn.setAttribute('data-state', 'stopped');
                toggleBtn.textContent = '开始学习';
            }
            UI.updateStatus('已停止');
            UI.log('⏹️ 学习流程已停止', 'warn');
        },
        
        // 跳过已完成课程的独立功能
        async skipCompletedCourses() {
            try {
                UI.log('🔍 开始检查并跳过已完成的课程...');
                UI.updateStatus('检查已完成课程');
                
                // 获取课程列表
                const courses = await API.getCourseList();
                if (!courses || courses.length === 0) {
                    UI.log('❌ 未找到课程列表', 'error');
                    return;
                }
                
                let completedCount = 0;
                
                for (let i = 0; i < courses.length; i++) {
                    const course = courses[i];
                    const courseId = course.id || course.courseId;
                    
                    UI.log(`检查第 ${i + 1}/${courses.length} 门课程: ${course.title}`);
                    
                    try {
                        const completionCheck = await API.checkCourseCompletion(courseId);
                        if (completionCheck.isCompleted) {
                            UI.log(`✅ 已完成: ${course.title} (${completionCheck.finishedRate}%)`, 'success');
                            completedCount++;
                        } else {
                            UI.log(`📖 未完成: ${course.title} (${completionCheck.finishedRate}%)`);
                        }
                    } catch (error) {
                        UI.log(`❌ 检查失败: ${course.title} - ${error.message}`, 'error');
                    }
                    
                    // 更新进度
                    const progress = Math.floor(((i + 1) / courses.length) * 100);
                    UI.updateProgress(progress);
                }
                
                UI.log(`\n📊 检查完成: ${completedCount}/${courses.length} 门课程已完成`, 'success');
                UI.updateStatus(`检查完成 - ${completedCount}/${courses.length} 已完成`);
                
            } catch (error) {
                UI.log(`❌ 检查过程出错: ${error.message}`, 'error');
                UI.updateStatus('检查失败');
            }
        },

        // 更新主学习流程 - 增强已完成课程检查和统计信息
        async processCourses(courses) {
            UI.log(`发现 ${courses.length} 门课程,开始处理...`);
            UI.updateStatus('处理课程列表');
            
            // 初始化统计信息
            const stats = {
                total: courses.length,
                completed: 0,
                learned: 0,
                failed: 0,
                skipped: 0
            };
            
            UI.updateStatistics(stats);
            
            for (let i = 0; i < courses.length; i++) {
                // 检查是否收到停止请求
                if (this.stopRequested) {
                    UI.log('⏹️ 收到停止请求,中断学习流程', 'warn');
                    break;
                }
                
                const course = courses[i];
                UI.log(`\n📚 处理第 ${i + 1}/${courses.length} 门课程: ${course.title}`);
                UI.updateStatus(`学习课程 ${i + 1}/${courses.length}`);
                
                try {
                    const courseId = course.id || course.courseId;
                    
                    // 首先检查课程是否已完成
                    if (CONFIG.SKIP_COMPLETED_COURSES) {
                        const completionCheck = await API.checkCourseCompletion(courseId);
                        if (completionCheck.isCompleted) {
                            UI.log(`✅ 课程已完成,跳过: ${course.title} (${completionCheck.finishedRate}%)`, 'success');
                            stats.completed++;
                            UI.updateStatistics(stats);
                            continue;
                        }
                    }
                    
                    // 获取课程播放信息
                    const playInfo = await API.getPlayInfo(courseId, course.dsUnitId, course.durationStr);
                    if (!playInfo) {
                        UI.log(`❌ 无法获取课程播放信息,跳过: ${course.title}`, 'error');
                        stats.failed++;
                        UI.updateStatistics(stats);
                        continue;
                    }
                    
                    // 双重检查:通过播放信息再次确认完成状态
                    const progressPercent = Math.floor((playInfo.lastLearnedTime / playInfo.duration) * 100);
                    if (progressPercent >= CONFIG.COMPLETION_THRESHOLD) {
                        UI.log(`✅ 播放信息确认课程已完成,跳过: ${course.title} (${progressPercent}%)`, 'success');
                        stats.completed++;
                        UI.updateStatistics(stats);
                        continue;
                    }
                    
                    // 开始学习课程
                    const courseInfoWithPlayInfo = {
                        ...course,
                        ...playInfo,
                        title: course.title || course.courseName,
                        courseId: courseId
                    };
                    
                    // 使用智能学习策略
                    const success = await API.smartLearnCourse(courseInfoWithPlayInfo);
                    
                    if (success) {
                        UI.log(`✅ 课程学习完成: ${course.title}`, 'success');
                        stats.learned++;
                    } else {
                        UI.log(`❌ 课程学习失败: ${course.title}`, 'error');
                        stats.failed++;
                    }
                    
                    UI.updateStatistics(stats);
                    
                    // 更新总体进度
                    const overallProgress = Math.floor(((i + 1) / courses.length) * 100);
                    UI.updateProgress(overallProgress);
                    
                    // 课程间隔等待(分段检查停止请求)
                    if (i < courses.length - 1) {
                        const delay = Math.random() * 8000 + 12000; // 12-20秒随机间隔
                        UI.log(`⏳ 等待 ${Math.round(delay/1000)} 秒后处理下一门课程...`);
                        
                        // 分段等待,每秒检查一次停止请求
                        const delaySeconds = Math.round(delay / 1000);
                        for (let j = 0; j < delaySeconds; j++) {
                            if (this.stopRequested) {
                                UI.log('⏹️ 等待期间收到停止请求,中断学习流程', 'warn');
                                return;
                            }
                            await new Promise(resolve => setTimeout(resolve, 1000));
                        }
                    }
                    
                } catch (error) {
                    UI.log(`❌ 处理课程 ${course.title} 时出错: ${error.message}`, 'error');
                    stats.failed++;
                    UI.updateStatistics(stats);
                    continue;
                }
            }
            
            // 显示学习统计
            UI.log(`\n🎉 所有课程处理完成!`, 'success');
            UI.log(`📊 学习统计:`);
            UI.log(`   ✅ 已完成课程: ${stats.completed} 门`);
            UI.log(`   📚 新学完课程: ${stats.learned} 门`);
            UI.log(`   ❌ 失败课程: ${stats.failed} 门`);
            UI.log(`   📖 总课程数: ${stats.total} 门`);
            
            UI.updateStatus(`完成 - ${stats.completed + stats.learned}/${stats.total} 门课程`);
            UI.updateProgress(100);
        },
        
        // 主流程(增强版)
        async startLearning() {
            try {
                // 重置停止标志并创建新的AbortController
                this.stopRequested = false;
                API.abortController = new AbortController();
                UI.log('开始学习流程...');
                
                // 获取课程列表
                const courses = await API.getCourseList();
                if (!courses || courses.length === 0) {
                    UI.log('❌ 未找到课程列表', 'error');
                    return;
                }
                
                // 使用新的课程处理方法
                await this.processCourses(courses);
                
                // 学习完成后重置按钮状态
                if (!this.stopRequested) {
                    const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
                    toggleBtn.setAttribute('data-state', 'stopped');
                    toggleBtn.textContent = '开始学习';
                    UI.updateStatus('学习完成');
                }
                
            } catch (error) {
                UI.log(`❌ 学习流程出错: ${error.message}`, 'error');
                console.error('学习流程错误:', error);
                
                // 出错时也要重置按钮状态
                const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
                toggleBtn.setAttribute('data-state', 'stopped');
                toggleBtn.textContent = '开始学习';
                UI.updateStatus('学习出错');
            }
        }
    };

    // --- 初始化 (v2.0优化) ---
    function init() {
        // 1. 加载用户配置
        Settings.load();
        
        // 2. 创建UI面板(会自动初始化事件监听器)
        UI.createPanel();
        
        // 3. 注册菜单命令
        GM_registerMenuCommand('导出调试日志', UI.exportLogs, 'e');
        
        // 4. 绑定主要控制按钮
        const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
        toggleBtn.addEventListener('click', () => {
            const isRunning = toggleBtn.getAttribute('data-state') === 'running';
            if (isRunning) {
                Learner.stop();
            } else {
                // 更新按钮状态
                toggleBtn.setAttribute('data-state', 'running');
                toggleBtn.textContent = '停止学习';
                
                // 使用事件驱动更新状态
                EventBus.publish('statusUpdate', '学习中...');
                
                // 启动学习流程
                Learner.startLearning().catch(error => {
                    EventBus.publish('log', { message: `❌ 启动学习流程失败: ${error.message}`, type: 'error' });
                    Learner.stop();
                });
            }
        });
        
        // 5. 发布初始化完成事件
        EventBus.publish('log', { message: '🚀 API学习助手 v3.37.0 初始化完成', type: 'success' });
        EventBus.publish('log', { message: '✨ 新特性: 事件驱动架构 + 用户配置界面 + 策略模式', type: 'info' });
    }

    // 初始化环境检测和脚本
    function initScript() {
        detectEnvironment();
        init();
    }
    
    setTimeout(initScript, 2000);

})();