精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

FastAPI開發AI應用教程:新增用戶歷史消息

人工智能
本教程通過前端會話 ID 管理、后端歷史消息接口和流式對話上下文傳遞三個核心技術,實現了支持多助手切換和歷史記錄持久化的 AI 聊天應用。?

本文將深入介紹如何在 FastAPI AI 聊天應用中實現用戶歷史消息功能,當用戶切換助手,刷新頁面時,都可以保留當前會話歷史消息。

圖片圖片

本項目已經開源至 Github,項目地址:https://github.com/wayn111/fastapi-ai-chat-demo

溫馨提示:本文全文約一萬字,看完約需 15 分鐘。

文章概述

重點講解每個助手區分 sessionid、獲取歷史消息接口以及發送消息時攜帶上下文信息的核心技術實現。通過本教程,你將掌握構建智能聊天應用中消息持久化和上下文管理的關鍵技術。

核心功能

  • 多助手會話隔離:每個 AI 助手(智能助手、AI 老師、編程專家)都有獨立的會話歷史
  • 智能會話管理:自動生成和管理 sessionid,確保會話的唯一性和持久性
  • 歷史消息加載:快速加載和展示用戶的歷史對話記錄
  • 上下文傳遞:發送消息時自動攜帶歷史上下文,保持對話連貫性
  • 數據持久化:支持 Redis 和內存兩種存儲方式

技術棧

  • 后端框架:FastAPI(高性能異步 Web 框架)
  • 數據存儲:Redis(主要)+ 內存存儲(備用)
  • 前端技術:原生 JavaScript + HTML5 + CSS3
  • 數據格式:JSON(消息序列化和傳輸)
  • 會話管理:UUID + 時間戳(會話 ID 生成)

核心架構設計

??? 數據模型設計

在實現歷史消息功能之前,我們需要設計合理的數據模型來存儲和管理消息數據:

@dataclass
class AIMessage:
    """AI消息數據類"""
    role: str
    content: str
    timestamp: float
    image_data: Optional[str] = None  # Base64編碼的圖片數據
    image_type: Optional[str] = None  # 圖片類型 (jpeg, png, gif)

這個數據類定義了消息的基本結構,包含角色、內容、時間戳和可選的圖片數據字段。

?? 會話 ID 管理策略

會話 ID 是整個歷史消息系統的核心,我們采用了前端生成、后端接收的管理策略:

前端會話 ID 生成邏輯:

// 前端生成會話ID的核心邏輯
if (sessionId) {
    // 復用已存在的會話ID
    currentSessionId = sessionId;
} else {
    // 生成新的會話ID:時間戳 + 隨機數
    const timestamp = Date.now();
    const randomNum = Math.floor(Math.random() * 10000);
    sessionId = `session_${timestamp}_${randomNum}`;
    currentSessionId = sessionId;
    localStorage.setItem(sessionKey, sessionId);
}

后端鍵名管理:

def get_conversation_key(user_id: str, session_id: str) -> str:
    """獲取對話在Redis中的鍵名"""
    return f"conversation:{user_id}:{session_id}"

def get_user_sessions_key(user_id: str) -> str:
    """獲取用戶會話列表在Redis中的鍵名"""
    return f"user_sessions:{user_id}"

前端生成唯一的會話 ID 并傳遞給后端,后端使用這個 ID 構建 Redis 鍵名來存儲對話數據。

核心功能實現

?? 功能一:每個助手區分 sessionid

前端實現:智能會話管理

在前端,我們為每個助手類型維護獨立的 sessionid,實現真正的會話隔離:

/**
 * 選擇智能助手類型
 * @param {string} assistantType - 助手類型
 */
function selectAssistant(assistantType) {
    // 更新當前助手類型
    currentAssistantType = assistantType;

    // 移除所有助手項的active類
    document.querySelectorAll('.assistant-item').forEach(item => {
        item.classList.remove('active');
    });

    // 為當前選中的助手添加active類
    event.target.closest('.assistant-item').classList.add('active');

    // 更新所有現有的assistant消息頭像
    updateAssistantAvatars(assistantType);

    // 從全局配置中獲取角色信息
    const roleConfig = aiRolesConfig[assistantType];
    if (!roleConfig) {
        console.error('未找到角色配置:', assistantType);
        return;
    }

    // 更新選中模型信息顯示
    updateSelectedModelInfo(assistantType);

    // 切換助手時處理sessionId
    const sessionKey = `${assistantType}_sessionId`;
    let sessionId = localStorage.getItem(sessionKey);

    if (sessionId) {
        // 如果該助手已有sessionId,使用之前的
        currentSessionId = sessionId;
    } else {
        // 如果沒有sessionId,生成新的
        const timestamp = Date.now();
        const randomNum = Math.floor(Math.random() * 10000);
        sessionId = `session_${timestamp}_${randomNum}`;
        currentSessionId = sessionId;
        localStorage.setItem(sessionKey, sessionId);
    }

    // 根據當前助手的sessionId重新調用history接口
    loadAssistantHistory(assistantType);
}

這個函數負責切換助手時的會話管理,為每個助手類型維護獨立的 sessionId,并從 localStorage 中獲取或生成新的會話 ID。

后端實現:接收會話 ID 并管理數據

后端接收前端傳來的會話 ID,通過 Redis 實現會話數據的持久化存儲:

async def save_message_to_redis(user_id: str, session_id: str, message: ChatMessage):
    """將消息保存到Redis或內存"""
    try:
        message_data = {
            "role": message.role,
            "content": message.content,
            "timestamp": message.timestamp,
            "image_data": getattr(message, 'image_data', None),
            "image_type": getattr(message, 'image_type', None)
        }

        if REDIS_AVAILABLE and redis_client:
            # Redis存儲:高性能,支持數據過期
            conversation_key = get_conversation_key(user_id, session_id)
            redis_client.lpush(conversation_key, json.dumps(message_data))
            redis_client.ltrim(conversation_key, 0, 19)  # 只保留最近20條消息
            redis_client.expire(conversation_key, 86400 * 7)  # 7天過期

            # 更新會話信息
            sessions_key = get_user_sessions_key(user_id)
            session_info = {
                "session_id": session_id,
                "last_message": message.content[:50] + "..."if len(message.content) > 50else message.content,
                "last_timestamp": message.timestamp
            }
            redis_client.hset(sessions_key, session_id, json.dumps(session_info))
            redis_client.expire(sessions_key, 86400 * 30)  # 30天過期

            logger.info(f"消息已保存到Redis - 用戶: {user_id}, 會話: {session_id[:8]}..., 角色: {message.role}")
        else:
            # 內存存儲:備用方案
            if user_id notin MEMORY_STORAGE["conversations"]:
                MEMORY_STORAGE["conversations"][user_id] = {}
            if session_id notin MEMORY_STORAGE["conversations"][user_id]:
                MEMORY_STORAGE["conversations"][user_id][session_id] = []

            MEMORY_STORAGE["conversations"][user_id][session_id].append(message_data)

            # 限制內存中的消息數量
            if len(MEMORY_STORAGE["conversations"][user_id][session_id]) > 20:
                MEMORY_STORAGE["conversations"][user_id][session_id] = \
                    MEMORY_STORAGE["conversations"][user_id][session_id][-20:]

            logger.info(f"消息已保存到內存 - 用戶: {user_id}, 會話: {session_id[:8]}..., 角色: {message.role}")

    except Exception as e:
        logger.error(f"保存消息失敗 - 用戶: {user_id}, 會話: {session_id[:8]}..., 錯誤: {e}")
        raise

這個函數將消息保存到 Redis 或內存中,支持雙重存儲策略,并設置了消息數量限制和過期時間。

?? 功能二:獲取歷史消息接口圖片

后端 API 設計

我們設計了一個高效的歷史消息獲取接口:

@app.get("/chat/history")
asyncdef get_chat_history(
    user_id: str = Query(..., descriptinotallow="用戶ID"),
    session_id: str = Query(..., descriptinotallow="會話ID")
):
    """獲取聊天歷史"""
    logger.info(f"獲取聊天歷史 - 用戶: {user_id}, 會話: {session_id[:8]}...")

    try:
        history = await get_conversation_history(user_id, session_id)
        logger.info(f"聊天歷史獲取成功 - 用戶: {user_id}, 會話: {session_id[:8]}..., 消息數: {len(history)}")
        return {
            "session_id": session_id,
            "messages": history,
            "total": len(history)
        }
    except Exception as e:
        logger.error(f"獲取聊天歷史失敗 - 用戶: {user_id}, 會話: {session_id[:8]}..., 錯誤: {e}")
        raise HTTPException(status_code=500, detail="獲取聊天歷史失敗")

asyncdef get_conversation_history(user_id: str, session_id: str) -> List[Dict[str, Any]]:
    """從Redis或內存獲取對話歷史"""
    try:
        if REDIS_AVAILABLE and redis_client:
            # 從Redis獲取
            conversation_key = get_conversation_key(user_id, session_id)
            messages = redis_client.lrange(conversation_key, 0, -1)

            # 反轉消息順序(Redis中是倒序存儲的)
            messages.reverse()

            history = [json.loads(msg) for msg in messages]
            logger.info(f"從Redis獲取對話歷史 - 用戶: {user_id}, 會話: {session_id[:8]}..., 消息數量: {len(history)}")
            return history
        else:
            # 從內存獲取
            if (user_id in MEMORY_STORAGE["conversations"] and
                session_id in MEMORY_STORAGE["conversations"][user_id]):
                history = MEMORY_STORAGE["conversations"][user_id][session_id]
                logger.info(f"從內存獲取對話歷史 - 用戶: {user_id}, 會話: {session_id[:8]}..., 消息數量: {len(history)}")
                return history
            else:
                logger.info(f"未找到對話歷史 - 用戶: {user_id}, 會話: {session_id[:8]}...")
                return []

    except Exception as e:
        logger.error(f"獲取對話歷史失敗 - 用戶: {user_id}, 會話: {session_id[:8]}..., 錯誤: {e}")
        return []

前端歷史消息加載

前端通過異步請求加載和渲染歷史消息:

/**
 * 加載指定助手的歷史消息
 * @param {string} assistantType - 助手類型
 */
asyncfunction loadAssistantHistory(assistantType) {
    try {
        // 獲取該助手的sessionId
        const sessionId = localStorage.getItem(`${assistantType}_sessionId`);
        if (!sessionId) {
            // 如果沒有sessionId,顯示歡迎消息
            showWelcomeMessage(assistantType);
            return;
        }

        // 更新當前會話ID
        currentSessionId = sessionId;

        // 清空當前聊天消息
        const chatMessages = document.getElementById('chatMessages');
        chatMessages.innerHTML = '';

        // 顯示加載提示
        const loadingMessage = document.createElement('div');
        loadingMessage.className = 'message assistant';
        loadingMessage.innerHTML = `
            <div class="message-avatar">??</div>
            <div class="message-content-wrapper">
                正在加載歷史消息...
            </div>
        `;
        chatMessages.appendChild(loadingMessage);

        // 從后端獲取歷史消息
        const response = await fetch(`/chat/history?session_id=${sessionId}&user_id=${userId}`);
        if (response.ok) {
            const data = await response.json();

            // 清空加載提示
            chatMessages.innerHTML = '';

            // 渲染歷史消息
            if (data.messages && data.messages.length > 0) {
                data.messages.forEach(message => {
                    renderHistoryMessage(message);
                });
                console.log(`加載了 ${data.messages.length}條歷史消息`);
            } else {
                // 如果沒有歷史消息,顯示歡迎消息
                showWelcomeMessage(assistantType);
            }

            // 滾動到底部
            scrollToBottom();
        } else {
            console.error('加載歷史消息失敗:', response.statusText);
            showWelcomeMessage(assistantType);
        }
    } catch (error) {
        console.error('加載助手歷史失敗:', error);
        showWelcomeMessage(assistantType);
    }
}

/**
 * 渲染歷史消息
 * @param {Object} message - 消息對象
 */
function renderHistoryMessage(message) {
    const chatMessages = document.getElementById('chatMessages');
    const messageDiv = document.createElement('div');
    messageDiv.className = `message ${message.role}`;

    // 創建頭像
    const avatarDiv = document.createElement('div');
    avatarDiv.className = 'message-avatar';

    // 如果是assistant消息,設置助手圖標
    if (message.role === 'assistant') {
        const icon = getAssistantIcon(currentAssistantType);
        avatarDiv.setAttribute('data-icon', icon);
    }

    const contentDiv = document.createElement('div');
    contentDiv.className = 'message-content-wrapper';

    // 處理消息內容
    if (message.role === 'assistant') {
        // 對于AI回復,使用Markdown渲染
        renderMarkdownContent(message.content, contentDiv);
    } else {
        // 對于用戶消息,檢查是否包含圖片
        if (message.image_data) {
            // 創建圖片元素
            const imageDiv = document.createElement('div');
            imageDiv.className = 'message-image';
            const img = document.createElement('img');
            img.src = `data:${message.image_type};base64,${message.image_data}`;
            img.alt = '用戶上傳的圖片';
            img.style.maxWidth = '300px';
            img.style.borderRadius = '8px';
            imageDiv.appendChild(img);
            contentDiv.appendChild(imageDiv);
        }

        // 添加文本內容
        if (message.content && message.content.trim()) {
            const textDiv = document.createElement('div');
            textDiv.textContent = message.content;
            contentDiv.appendChild(textDiv);
        }
    }

    messageDiv.appendChild(avatarDiv);
    messageDiv.appendChild(contentDiv);
    chatMessages.appendChild(messageDiv);
}

這個函數從后端獲取指定助手的歷史消息,并在前端進行渲染顯示,支持文本和圖片消息的完整展示。

?? 功能三:發送消息時攜帶上下文信息

后端流式對話實現

發送消息時,我們需要獲取歷史上下文并傳遞給 AI 模型:

1. 流式聊天接口
@app.post("/chat/stream")
asyncdef chat_stream(request: ChatRequest):
    """流式聊天接口"""
    # 設置默認值
    role = "assistant"
    provider = request.provider
    model = getattr(request, 'model', None)

    logger.info(f"流式聊天請求 - 用戶: {request.user_id}, 會話: {request.session_id[:8]}..., 角色: {role}, 消息長度: {len(request.message)}, 提供商: {provider}")

    if role notin AI_ROLES:
        logger.warning(f"不支持的AI角色: {role}")
        raise HTTPException(status_code=400, detail="不支持的AI角色")

    return StreamingResponse(
        generate_streaming_response(request.user_id, request.session_id, request.message, role, provider, model, request.image_data, request.image_type),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Access-Control-Allow-Origin": "*"
        }
    )

這個接口是流式聊天的入口點:

  • 接收前端發送的 ChatRequest 對象,包含用戶 ID、會話 ID、消息內容等
  • 設置默認的 AI 角色為 "assistant",從請求中獲取 AI 提供商和模型信息
  • 驗證 AI 角色是否在支持的角色列表中
  • 返回 StreamingResponse 對象,設置 SSE(Server-Sent Events)相關的響應頭
  • 調用 generate_streaming_response 函數處理具體的流式響應邏輯
2. 流式響應生成函數
async def generate_streaming_response(user_id: str, session_id: str, user_message: str, role: str = "assistant", provider: Optional[str] = None, model: Optional[str] = None, image_data: Optional[str] = None, image_type: Optional[str] = None):
    """生成流式響應"""
    logger.info(f"開始流式響應 - 用戶: {user_id}, 會話: {session_id[:8]}..., 角色: {role}, 消息長度: {len(user_message)}, 提供商: {provider}")

    try:
        # 1. 保存用戶消息到Redis
        from ai_providers.base import AIMessage
        user_msg = AIMessage(
            role="user",
            cnotallow=user_message,
            timestamp=time.time(),
            image_data=image_data,
            image_type=image_type
        )
        await save_message_to_redis(user_id, session_id, user_msg)

        # 2. 獲取對話歷史記錄
        history = await get_conversation_history(user_id, session_id)

        # 3. 構建系統提示詞
        system_prompt = AI_ROLES.get(role, AI_ROLES["assistant"])["prompt"]

        # 4. 構建AI消息對象列表
        ai_messages = []

        # 5. 添加歷史消息(限制數量避免上下文過長)
        recent_messages = history[-config.MAX_HISTORY_MESSAGES:] if len(history) > config.MAX_HISTORY_MESSAGES else history
        for msg in recent_messages:
            if msg["role"] in ["user", "assistant"]:
                ai_messages.append(AIMessage(
                    role=msg["role"],
                    cnotallow=msg["content"],
                    timestamp=msg.get("timestamp", time.time()),
                    image_data=msg.get("image_data"),
                    image_type=msg.get("image_type")
                ))

        # 6. 調用AI提供商的流式API
        logger.info(f"調用AI流式API - 消息數: {len(ai_messages)}, 提供商: {provider or '默認'}, 模型: {model or '默認'}")

        full_response = ""
        content_only_response = ""# 只保存 type: 'content' 的內容
        chunk_count = 0

        # 7. 處理流式響應
        asyncfor chunk in ai_manager.generate_streaming_response(
            messages=ai_messages,
            provider=provider,
            model=model,
            system_prompt=system_prompt
        ):
            if chunk:
                full_response += chunk
                chunk_count += 1

                # 8. 解析chunk數據,過濾出純文本內容
                try:
                    if chunk.startswith("data: "):
                        json_str = chunk[6:].strip()  # 移除 "data: " 前綴
                        if json_str:
                            chunk_data = json.loads(json_str)
                            # 只累積 type 為 'content' 的內容用于保存到Redis
                            if chunk_data.get('type') == 'content'and'content'in chunk_data:
                                content_only_response += chunk_data['content']
                except (json.JSONDecodeError, KeyError) as e:
                    # 如果解析失敗,按原來的方式處理(向后兼容)
                    logger.debug(f"解析chunk數據失敗,使用原始內容: {e}")
                    content_only_response += chunk

                # 9. 實時推送數據到前端
                yield chunk

        logger.info(f"流式響應完成 - 用戶: {user_id}, 會話: {session_id[:8]}..., 塊數: {chunk_count}, 總長度: {len(full_response)}, 內容長度: {len(content_only_response)}")

        # 10. 保存AI響應到Redis(只保存純文本內容)
        ai_msg = ChatMessage(
            role="assistant",
            cnotallow=content_only_response,  # 使用過濾后的內容
            timestamp=time.time()
        )
        await save_message_to_redis(user_id, session_id, ai_msg)

        # 11. 發送結束信號
        yieldf"data: {json.dumps({'type': 'end', 'session_id': session_id})}\n\n"

    except Exception as e:
        logger.error(f"流式響應錯誤 - 用戶: {user_id}, 會話: {session_id[:8]}..., 錯誤: {e}")
        error_msg = f"抱歉,服務出現錯誤:{str(e)}"
        yieldf"data: {json.dumps({'content': error_msg, 'type': 'error'})}\n\n"

這個函數是流式響應的核心實現,主要包含以下步驟:

  1. 保存用戶消息:將用戶發送的消息(包括文本和圖片)保存到 Redis 中
  2. 獲取歷史記錄:根據用戶 ID 和會話 ID 從 Redis 中獲取完整的對話歷史
  3. 構建系統提示:根據 AI 角色獲取對應的系統提示詞
  4. 構建消息列表:將歷史消息轉換為 AI 模型需要的格式
  5. 限制歷史長度:只取最近的 N 條消息,避免上下文過長影響性能
  6. 調用 AI API:使用 AI 管理器調用指定提供商的流式 API
  7. 處理流式數據:逐塊接收 AI 響應,實時推送給前端
  8. 數據過濾:從流式數據中提取純文本內容,用于保存到數據庫
  9. 實時推送:使用 yield 將數據塊實時發送給前端
  10. 保存 AI 響應:將完整的 AI 回復保存到 Redis 中
  11. 發送結束信號:通知前端流式響應已完成

通過這種設計,實現了帶有完整上下文的流式對話功能,用戶可以看到 AI 的實時回復,同時所有對話記錄都會被持久化保存。

總結

本教程通過前端會話 ID 管理、后端歷史消息接口和流式對話上下文傳遞三個核心技術,實現了支持多助手切換和歷史記錄持久化的 AI 聊天應用。

責任編輯:武曉燕 來源: 程序員wayn
相關推薦

2025-07-28 01:55:00

2025-09-18 06:56:02

2025-07-09 08:11:38

AIFastAPI開發

2025-07-09 02:11:00

2025-07-14 07:30:00

2025-07-04 00:00:00

2011-07-08 14:58:16

iPhone Xcode iOS

2011-07-28 09:58:31

Web

2011-08-09 13:10:32

iPhone地圖開發

2021-01-19 12:46:45

鴻蒙HarmonyOSHelloworld

2025-05-09 06:30:52

2011-11-17 13:29:44

Android用戶體驗導向

2011-08-10 10:23:20

iPhoneArchivingNSCoder

2011-08-08 18:19:09

iPhone音頻播放

2011-08-24 13:56:12

Lua游戲

2024-11-26 07:33:09

2012-11-20 10:04:46

Winform開發

2021-01-25 09:58:01

鴻蒙HarmonyOS應用開發

2024-11-26 09:50:18

AIjs 工具庫前端開發
點贊
收藏

51CTO技術棧公眾號

欧美xxx网站| 成人乱码一区二区三区| japanese国产精品| 欧美日韩一卡二卡| 欧美一级爱爱视频| 欧美日本网站| 国内成+人亚洲+欧美+综合在线| 欧美激情乱人伦一区| 波多野结衣av在线观看| www.久久爱.com| 黄网站色欧美视频| 亚洲巨乳在线观看| 午夜视频免费看| 极品尤物av久久免费看| 国内精品伊人久久| 欧美日韩国产一二三区| 黄色av免费播放| 国产福利一区二区三区在线播放| 亚洲制服欧美中文字幕中文字幕| 亚洲人免费视频| 中日韩午夜理伦电影免费| 黄色在线视频网| 超碰91在线观看| 中文字幕一区二区三区不卡| 久久99精品久久久久久秒播放器| 97超碰人人模人人人爽人人爱| 亚洲一区二区三区高清| 欧美乱妇高清无乱码| av手机在线播放| 色婷婷综合久久久久久| 欧美一二三四区在线| 亚洲免费看av| 成人在线爆射| 欧美性xxxxx极品娇小| 男人天堂手机在线视频| 麻豆影视国产在线观看| 久久精品人人做人人爽97| 国产视频一区二区三区四区| 精品人妻少妇AV无码专区| 久久成人免费网| 国产精品精品国产| 在线精品免费视| 免费日韩视频| 91av网站在线播放| 国产欧美日韩另类| 悠悠资源网久久精品| 欧美大片欧美激情性色a∨久久| 五月天色婷婷丁香| 99re6这里只有精品| 一本大道亚洲视频| 2019男人天堂| 久久综合99| 老鸭窝毛片一区二区三区| 亚洲性xxxx| 久久精品成人av| 青娱乐国产在线视频| 国产成人天天5g影院在线观看| 91在线丨porny丨国产| 春色成人在线视频| 亚洲第一天堂在线观看| 国产成人在线视频网站| 成人有码在线视频| 国产乱淫av免费| 国产精一品亚洲二区在线视频| 91久久久亚洲精品| 国产精品无码久久久久成人app| 久久97超碰国产精品超碰| 国产在线视频91| av天堂一区二区三区| 国产精品69毛片高清亚洲| 99久久99久久精品国产片| 亚洲春色一区二区三区| 成人午夜av电影| 久久精品国产理论片免费| 免费福利在线观看| 中文字幕乱码亚洲精品一区| 日本免费在线视频观看| 调教一区二区| 欧美日韩久久久久| 国产又黄又猛又粗又爽的视频| 欧洲亚洲精品久久久久| 日韩午夜电影av| 老熟妇精品一区二区三区| 久久综合影院| 精品国产一区二区三区久久| 久久久久久久国产视频| 欧美专区18| 成人在线视频网站| 欧美一区二区三区激情| 国产丝袜美腿一区二区三区| 最新欧美日韩亚洲| 韩国精品一区| 欧美日韩精品一区二区三区蜜桃| 久久发布国产伦子伦精品| 日韩动漫一区| 北条麻妃一区二区三区中文字幕 | 欧美午夜大胆人体| 亚洲尤物视频在线| 亚洲黄色a v| 97久久精品| 一本色道久久综合亚洲精品小说| 最新一区二区三区| 欧美亚洲一区二区三区| 91成人理论电影| 国产午夜在线观看| 亚洲一区二区三区激情| 亚洲一二三区av| 爱高潮www亚洲精品| 自拍亚洲一区欧美另类| 国产午夜在线播放| 激情综合色播五月| 欧美日韩另类丝袜其他| yellow91字幕网在线| 日韩欧美综合在线视频| 在线播放av网址| 欧美1级片网站| 欧美重口另类videos人妖| 精品国产区一区二| 欧美激情一区二区三区不卡| 国产白丝袜美女久久久久| 成人永久在线| 在线丨暗呦小u女国产精品| 日韩欧美亚洲视频| 国产一二精品视频| 色噜噜狠狠色综合网| 手机在线观看av| 日韩精品在线看片z| 亚洲欧美卡通动漫| 久久艹免费视频| 亚洲精品乱码| 91在线视频免费| 91在线播放网站| 一本一道久久a久久精品| 亚洲欧美高清在线| 一区二区三区中文| 国产欧美在线播放| 成人欧美亚洲| 欧美在线一二三四区| 免费在线观看你懂的| 夜夜嗨一区二区| 国产亚洲欧美一区二区| 免费电影网站在线视频观看福利| 这里只有精品视频在线观看| 99热6这里只有精品| 日本女人一区二区三区| 日本一区免费在线观看| 日产精品一区| 在线a欧美视频| 国产成人无码专区| 国产视频一区二区三区在线观看| 男女高潮又爽又黄又无遮挡| 欧美变态挠脚心| 55夜色66夜色国产精品视频| 三级理论午夜在线观看| 欧美午夜影院在线视频| 欧美成人国产精品一区二区| 日韩av中文字幕一区二区三区| 日韩电影在线播放| 国产乱子精品一区二区在线观看| 色偷偷综合社区| 96日本xxxxxⅹxxx17| 亚洲美女偷拍久久| 亚洲成年人av| 亚洲欧美日韩国产| 亚洲不卡一卡2卡三卡4卡5卡精品| 欧美sm一区| 国产午夜一区二区| 国产普通话bbwbbwbbw| 亚洲资源在线观看| 一起草在线视频| 欧美aa在线视频| 视频一区二区视频| 影音先锋国产在线| 国内精品美女在线观看| 国产一区二区三区黄| 中文一区一区三区高中清不卡免费| 亚洲免费精彩视频| 亚洲一区在线观| 亚洲影视在线播放| 一级性生活大片| 日本成人中文字幕在线视频| 最新欧美日韩亚洲| 久久99精品国产自在现线| 热99精品里视频精品| 午夜在线免费观看视频| 欧美xxx久久| 久久久黄色大片| 成人免费在线视频| 亚洲欧美日韩偷拍| 久久综合伊人| 日韩精品手机在线观看| 日韩精品福利一区二区三区| 国产裸体写真av一区二区 | 中文字幕一区二区三中文字幕| 最新日本中文字幕| 日本美女视频一区二区| www.欧美黄色| 久久国产影院| 精品久久蜜桃| 99视频有精品高清视频| 国产91精品黑色丝袜高跟鞋| 免费a在线看| 亚洲欧美国产精品久久久久久久| 国产精品久久久久精| 欧美色视频日本高清在线观看| 制服丨自拍丨欧美丨动漫丨| 91麻豆精品秘密| 真实乱偷全部视频| 日本不卡123| 国产精品自拍偷拍| 九一在线免费观看| 国产不卡一区视频| 波多结衣在线观看| 亚洲在线视频| r级无码视频在线观看| 日韩一区电影| 欧美大香线蕉线伊人久久国产精品| 国产精品亚洲综合在线观看| 国产极品精品在线观看| 韩日毛片在线观看| 久久99精品国产99久久6尤物| 国产高清在线观看| 日韩国产欧美精品在线| 亚洲国产中文字幕在线| 欧美精品粉嫩高潮一区二区| 天天干,天天干| 欧美日韩一二三四五区| 国产真实乱偷精品视频| 亚洲人成网站在线| 女人黄色一级片| 久久久不卡网国产精品二区| 国产在线观看无码免费视频| 丰满放荡岳乱妇91ww| 亚洲欧美天堂在线| 麻豆久久久久久| 国产精品天天av精麻传媒| 另类av一区二区| 欧美成人一区二区在线观看| 亚洲国产日韩在线| 亚洲色欲久久久综合网东京热| 国内成人在线| 91动漫在线看| 亚洲乱码视频| 伊人成色综合网| 亚洲一区二区三区高清| 欧美极品欧美精品欧美图片| 免费在线亚洲欧美| 人妻有码中文字幕| 鲁大师影院一区二区三区| 久久久久久久久久福利| 久久精品国产清高在天天线| 久久无码高潮喷水| 蜜桃视频一区| 亚洲精品一二三四五区| 另类成人小视频在线| 在线一区二区不卡| 国产成人午夜精品5599| 亚洲色图欧美日韩| 久久在线观看免费| 久久成人激情视频| 中文字幕一区二区三区四区不卡 | 亚洲国产精品久久不卡毛片 | 免费在线看黄| 美日韩精品免费观看视频| 欧美大片黄色| 97热在线精品视频在线观看| 亚洲同志男男gay1069网站| 国产97在线播放| 四虎国产精品免费久久5151| 91精品国产综合久久久久久丝袜| 成人黄色av网址| 蜜桃av色综合| 四虎8848精品成人免费网站| 91成人综合网| 久久国产精品久久久久久电车| 亚洲精品高清无码视频| 国产精品伊人色| 超碰97在线资源站| 国产精品欧美一区二区三区| 欧美日韩人妻精品一区二区三区| 亚洲成av人在线观看| 日韩免费av网站| 日韩欧美一二区| 女人天堂在线| 欧美老女人性生活| 97se综合| 亚洲综合色激情五月| 日本在线中文字幕一区| 在线不卡视频一区二区| 樱桃成人精品视频在线播放| 亚洲精品中文字幕无码蜜桃| 国产一区二区伦理片| 久久午夜夜伦鲁鲁片| 中文字幕二三区不卡| 精品少妇久久久| 欧美在线制服丝袜| 五月激情丁香婷婷| www.亚洲免费视频| 最近高清中文在线字幕在线观看1| 国产一区二区色| 中文字幕精品影院| 久久精品无码中文字幕| 久久99精品国产91久久来源| 午夜男人的天堂| 亚洲欧美日韩国产手机在线| 天天干天天干天天| 欧美成人女星排名| 日本中文字幕电影在线免费观看| 午夜精品视频在线| 高清久久精品| 日韩精品久久一区二区三区| 日韩亚洲精品在线| 欧美体内she精高潮| 国产精品美女久久久久久久久久久 | 91av网站在线播放| 人人爱人人干婷婷丁香亚洲| 视频一区视频二区视频| 亚洲影院在线| 亚洲麻豆一区二区三区| |精品福利一区二区三区| 免费黄色片视频| 国产视频久久久久| 2021中文字幕在线| 可以看av的网站久久看| 奇米777在线视频| 欧美国产乱子伦| 高清乱码免费看污| 亚洲国内精品在线| av中文在线资源库| 国产aⅴ精品一区二区三区黄| 午夜精品视频一区二区三区在线看| 日韩欧美在线免费观看视频| www一区二区| 日韩 欧美 综合| 亚洲成人国产精品| 麻豆蜜桃在线| 国产高清在线一区| 国产字幕视频一区二区| 宇都宫紫苑在线播放| 亚洲免费av高清| 999av视频| 欧美成人精品不卡视频在线观看| 国产日韩欧美中文在线| 香蕉视频在线网址| 精品亚洲porn| 少妇久久久久久被弄高潮| 欧美一区二区三区四区久久| 国内外激情在线| 91系列在线播放| 欧美日韩国产免费观看| 91精品国产高清91久久久久久| 亚洲图片你懂的| 超碰在线人人干| 欧美精品xxx| 亚洲+小说+欧美+激情+另类| av观看免费在线| 国产人成亚洲第一网站在线播放 | 日本乱理伦在线| 国产精品日韩一区二区| 亚洲精选久久| 人妻aⅴ无码一区二区三区| 欧美日韩中文字幕一区| 欧美边添边摸边做边爱免费| 亚洲一区二区久久久久久| 午夜精品电影| 色狠狠一区二区三区香蕉| 韩国中文字幕hd久久精品| 97超级碰碰碰| 凹凸成人精品亚洲精品密奴| 亚洲精品永久视频| 亚洲无线码一区二区三区| 亚洲色图另类小说| 国产精品久久久久久久久影视| 99精品在线| 制服丝袜在线第一页| 狠狠躁夜夜躁人人爽超碰91| 北岛玲一区二区三区| 亚洲va久久久噜噜噜| 在线视频精品| www.4hu95.com四虎| 精品三级在线观看| 日韩脚交footjobhd| 亚洲综合视频一区| 成人亚洲一区二区一| 色老头一区二区| 成人97在线观看视频| 精品自拍偷拍| 亚洲免费999| 五月婷婷激情综合网| 91涩漫在线观看| 国产91aaa| 免费成人小视频| 国产 日韩 欧美 成人| 在线色欧美三级视频| av成人资源| 色婷婷狠狠18| 欧美日韩中文在线观看| 3d玉蒲团在线观看| 欧美日韩精品免费观看| 高清不卡在线观看av|