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

SpringBoot與Quartz整合,實現訂單自動取消功能

開發 前端
@Scheduled 的限制:任務信息(何時執行、執行什么)僅存在于內存中。如果應用重啟,所有任務的調度狀態都會丟失。你需要手動重新配置和啟動它們。這對于需要長期運行或不能中斷的任務來說是不可接受的。

當遇到用戶下單后需在規定時間內完成支付的這種需求的時候,若用戶未支付,訂單將長時間占用庫存或資源,影響其他用戶購買并降低運營效率。為解決此問題,就有了"訂單自動取消"功能。該功能支持靈活配置檢查頻率與超時時間,并可動態管理任務啟停,實現自動化運營,自動識別并取消超時未支付的訂單,從而釋放資源,提升系統效率與用戶體驗。

我們為什么選擇Quartz?

雖然 Spring Boot 自帶的 @Scheduled 注解對于簡單的、單機、內存中的定時任務非常方便,但 Quartz 提供了幾個 @Scheduled 無法比擬的關鍵優勢,這些優勢對于構建一個健壯、可管理、生產就緒的定時任務系統至關重要。

任務持久化 (Persistence):

  • @Scheduled 的限制:任務信息(何時執行、執行什么)僅存在于內存中。如果應用重啟,所有任務的調度狀態都會丟失。你需要手動重新配置和啟動它們。這對于需要長期運行或不能中斷的任務來說是不可接受的。
  • Quartz 的優勢: Quartz 可以將任務(JobDetail)、觸發器(Trigger)和調度器狀態持久化到數據庫(如我們項目中使用的 MySQL)。這意味著:
  1. 重啟恢復: 應用重啟后,Quartz 會從數據庫中讀取之前存儲的任務和觸發器信息,自動恢復調度。那些在應用關閉期間“錯過”的執行,可以根據配置策略(如 MISFIRE_INSTRUCTION)進行處理。
  2. 狀態一致性: 任務的狀態(下次執行時間、是否暫停等)是持久化的,不會因為應用生命周期而丟失。

動態任務管理:

  • @Scheduled 的限制: 任務的執行計劃(Cron表達式等)通常在代碼中通過注解硬編碼,或者通過配置文件定義。要在運行時動態地創建、修改、暫停、恢復或刪除一個任務是非常困難甚至不可能的。
  • Quartz 的優勢: 提供了豐富的 API (Scheduler 接口) 來實現任務的全生命周期管理。正如我們的項目所展示的:
  1. 可以通過 REST API (OrderCleanupJobController) 動態地創建 (scheduleJob) 一個帶有特定 Cron 表達式和參數(如超時時間)的任務。
  2. 可以隨時暫停 (pauseJob)、恢復 (resumeJob) 或刪除 (deleteJob) 一個正在運行或已存在的任務。
  3. 這種靈活性對于運營、運維或業務配置至關重要,無需停機即可調整任務策略。

豐富的任務和觸發器模型:

  • Quartz 提供了比 @Scheduled 更精細和強大的任務(Job)和觸發器(Trigger)模型。支持多種觸發器類型(CronTrigger, SimpleTrigger 等),以及更復雜的調度需求。

代碼實操

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>quartz-order-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>quartz-order-demo</name>
    <description>Demo project for Spring Boot, Quartz, and Scheduled Order Cleanup using MySQL</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- MySQL Driver -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

spring:
  datasource:
    url:jdbc:mysql://localhost:3306/quartz_order_demo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username:root
    password:123456
    driver-class-name:com.mysql.cj.jdbc.Driver
jpa:
    database-platform:org.hibernate.dialect.MySQLDialect
    hibernate:
      ddl-auto:update# Hibernate會根據實體自動創建/更新表結構
    show-sql:true# 顯示執行的SQL語句,方便調試
    properties:
      hibernate:
        format_sql:true# 格式化SQL輸出

logging:
level:
    root:INFO
    com.example.quartzorder:DEBUG
    org.springframework.scheduling.quartz:INFO
    org.hibernate.SQL:DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder:TRACE# 顯示SQL參數值

# Quartz 使用數據庫存儲任務信息
spring:
quartz:
    job-store-type:jdbc# 使用 JDBC 存儲
    jdbc:
      initialize-schema:always# 啟動時總是初始化Quartz表 (生產環境慎用)
    properties:
      org:
        quartz:
          scheduler:
            instanceName:OrderCleanupScheduler
            instanceId:AUTO
          jobStore:
            class:org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix:QRTZ_
            isClustered:false
          threadPool:
            class:org.quartz.simpl.SimpleThreadPool
            threadCount:5

logback-spring.xml 日志配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>

    <logger name="com.example.quartzorder" level="DEBUG"/>
    <logger name="org.springframework.scheduling.quartz" level="INFO"/>
    <logger name="org.hibernate.SQL" level="DEBUG"/>
    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>

</configuration>

Order 實體類

package com.example.quartzorder.entity;

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "orders")
publicclass Order {

    publicenum Status {
        PENDING_PAYMENT, PAID, SHIPPED, CANCELLED
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String orderNumber;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Status status;

    @Column(name = "created_at", nullable = false)
    private LocalDateTime createdAt;

    // Constructors
    public Order() {}

    public Order(String orderNumber, Status status, LocalDateTime createdAt) {
        this.orderNumber = orderNumber;
        this.status = status;
        this.createdAt = createdAt;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(LocalDateTime createdAt) {
        this.createdAt = createdAt;
    }

    @Override
    public String toString() {
        return"Order{" +
                "id=" + id +
                ", orderNumber='" + orderNumber + '\'' +
                ", status=" + status +
                ", createdAt=" + createdAt +
                '}';
    }
}

Order Repository

package com.example.quartzorder.repository;

import com.example.quartzorder.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Repository
publicinterface OrderRepository extends JpaRepository<Order, Long> {

    /**
     * 查找狀態為 PENDING_PAYMENT 且創建時間早于指定時間的訂單
     * @param beforeTime 指定的時間點
     * @return 訂單列表
     */
    @Query("SELECT o FROM Order o WHERE o.status = 'PENDING_PAYMENT' AND o.createdAt < :beforeTime")
    List<Order> findPendingPaymentOrdersBefore(@Param("beforeTime") LocalDateTime beforeTime);

    /**
     * 批量更新訂單狀態為 CANCELLED
     * @param orderIds 要更新的訂單ID列表
     * @return 更新的行數
     */
    @Modifying
    @Transactional
    @Query("UPDATE Order o SET o.status = 'CANCELLED' WHERE o.id IN :orderIds")
    int cancelOrdersByIds(@Param("orderIds") List<Long> orderIds);
}

JobDataMapUtil 工具類

package com.example.quartzorder.util;

import org.quartz.JobDataMap;

publicclass JobDataMapUtil {

    publicstaticfinal String TIMEOUT_MINUTES_KEY = "timeoutMinutes";

    public static int getTimeoutMinutes(JobDataMap jobDataMap) {
        return jobDataMap.getInt(TIMEOUT_MINUTES_KEY);
    }

    public static void setTimeoutMinutes(JobDataMap jobDataMap, int timeoutMinutes) {
        jobDataMap.put(TIMEOUT_MINUTES_KEY, timeoutMinutes);
    }
}

訂單業務邏輯服務

package com.example.quartzorder.service;

import com.example.quartzorder.entity.Order;
import com.example.quartzorder.repository.OrderRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
publicclass OrderService {

    privatestaticfinal Logger logger = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private OrderRepository orderRepository;

    /**
     * 查找并取消超時的未支付訂單
     * @param timeoutMinutes 超時分鐘數
     */
    public void cancelUnpaidOrders(int timeoutMinutes) {
        LocalDateTime beforeTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
        logger.info("Searching for PENDING_PAYMENT orders created before {}", beforeTime);

        List<Order> ordersToCancel = orderRepository.findPendingPaymentOrdersBefore(beforeTime);

        if (ordersToCancel.isEmpty()) {
            logger.info("No PENDING_PAYMENT orders found to cancel.");
            return;
        }

        List<Long> orderIds = ordersToCancel.stream().map(Order::getId).collect(Collectors.toList());
        logger.info("Found {} orders to cancel: {}", ordersToCancel.size(), orderIds);

        // 執行批量更新
        int updatedCount = orderRepository.cancelOrdersByIds(orderIds);
        logger.info("Cancelled {} orders.", updatedCount);
        // 模擬:打印被取消的訂單號
        ordersToCancel.forEach(order -> System.out.println(">>> Order Cancelled: " + order.getOrderNumber()));
    }
}

Quartz Job類

package com.example.quartzorder.job;

import com.example.quartzorder.service.OrderService;
import com.example.quartzorder.util.JobDataMapUtil;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// 為了讓 @Autowired 生效,需要配置 SpringBeanJobFactory
@Component
publicclass CancelUnpaidOrdersJob implements Job {

    privatestaticfinal Logger logger = LoggerFactory.getLogger(CancelUnpaidOrdersJob.class);

    // 需要配合自定義的 SpringBeanJobFactory 使用
    @Autowired
    private OrderService orderService;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        int timeoutMinutes = JobDataMapUtil.getTimeoutMinutes(jobDataMap);
        String jobName = context.getJobDetail().getKey().getName();

        logger.info("Executing job [{}] with timeout [{}] minutes", jobName, timeoutMinutes);

        if (orderService != null) {
            orderService.cancelUnpaidOrders(timeoutMinutes);
        } else {
            logger.error("OrderService is not injected. Cannot execute job [{}]", jobName);
            // 在實際項目中,應確保 JobFactory 配置正確
        }
    }
}

配置正確的 JobFactory

package com.example.quartzorder.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;

@Component
publicclass AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

    privatetransient AutowireCapableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(final ApplicationContext context) {
        beanFactory = context.getAutowireCapableBeanFactory();
    }

    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}

application.yml

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always
    job-factory: com.example.quartzorder.config.AutowiringSpringBeanJobFactory

DTO

package com.example.quartzorder.model;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

publicclass OrderCleanupJobRequest {

    @NotBlank(message = "Job name cannot be blank")
    private String jobName;

    @NotBlank(message = "Cron expression cannot be blank")
    private String cronExpression;

    @Min(value = 1, message = "Timeout minutes must be at least 1")
    privateint timeoutMinutes = 10; // 默認10分鐘

    // Getters and Setters
    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public int getTimeoutMinutes() {
        return timeoutMinutes;
    }

    public void setTimeoutMinutes(int timeoutMinutes) {
        this.timeoutMinutes = timeoutMinutes;
    }
}

Service

package com.example.quartzorder.service;

import com.example.quartzorder.job.CancelUnpaidOrdersJob;
import com.example.quartzorder.util.JobDataMapUtil;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
publicclass OrderCleanupJobService {

    privatestaticfinal Logger logger = LoggerFactory.getLogger(OrderCleanupJobService.class);

    @Autowired
    private Scheduler scheduler;

    public void addJob(String jobName, String cronExpression, int timeoutMinutes) throws SchedulerException {
        if (scheduler.checkExists(JobKey.jobKey(jobName))) {
            logger.warn("Job [{}] already exists.", jobName);
            thrownew SchedulerException("Job already exists: " + jobName);
        }

        JobDataMap jobDataMap = new JobDataMap();
        JobDataMapUtil.setTimeoutMinutes(jobDataMap, timeoutMinutes);

        JobDetail jobDetail = JobBuilder.newJob(CancelUnpaidOrdersJob.class)
                .withIdentity(jobName)
                .usingJobData(jobDataMap)
                .build();

        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .forJob(jobDetail)
                .withIdentity(jobName + "_trigger")
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();

        scheduler.scheduleJob(jobDetail, cronTrigger);
        logger.info("Scheduled order cleanup job [{}] with cron [{}] and timeout [{}] minutes", jobName, cronExpression, timeoutMinutes);
    }

    public void deleteJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        if (!scheduler.checkExists(jobKey)) {
            logger.warn("Job [{}] does not exist.", jobName);
            thrownew SchedulerException("Job does not exist: " + jobName);
        }
        scheduler.deleteJob(jobKey);
        logger.info("Deleted order cleanup job [{}]", jobName);
    }

    public void pauseJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        if (!scheduler.checkExists(jobKey)) {
            logger.warn("Job [{}] does not exist.", jobName);
            thrownew SchedulerException("Job does not exist: " + jobName);
        }
        scheduler.pauseJob(jobKey);
        logger.info("Paused order cleanup job [{}]", jobName);
    }

    public void resumeJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        if (!scheduler.checkExists(jobKey)) {
            logger.warn("Job [{}] does not exist.", jobName);
            thrownew SchedulerException("Job does not exist: " + jobName);
        }
        scheduler.resumeJob(jobKey);
        logger.info("Resumed order cleanup job [{}]", jobName);
    }
}

Controller

package com.example.quartzorder.controller;

import com.example.quartzorder.model.OrderCleanupJobRequest;
import com.example.quartzorder.service.OrderCleanupJobService;
import jakarta.validation.Valid;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/order-cleanup-jobs")
publicclass OrderCleanupJobController {

    @Autowired
    private OrderCleanupJobService jobService;

    @PostMapping("/schedule")
    public ResponseEntity<Map<String, Object>> scheduleJob(@Valid@RequestBody OrderCleanupJobRequest request) {
        Map<String, Object> response = new HashMap<>();
        try {
            jobService.addJob(request.getJobName(), request.getCronExpression(), request.getTimeoutMinutes());
            response.put("status", "success");
            response.put("message", "Order cleanup job '" + request.getJobName() + "' scheduled successfully.");
            return ResponseEntity.status(HttpStatus.CREATED).body(response);
        } catch (SchedulerException e) {
            response.put("status", "error");
            response.put("message", "Failed to schedule job: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        } catch (Exception e) {
             response.put("status", "error");
             response.put("message", "Invalid request data: " + e.getMessage());
             return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
        }
    }

    @DeleteMapping("/delete/{jobName}")
    public ResponseEntity<Map<String, Object>> deleteJob(@PathVariable String jobName) {
        Map<String, Object> response = new HashMap<>();
        try {
            jobService.deleteJob(jobName);
            response.put("status", "success");
            response.put("message", "Order cleanup job '" + jobName + "' deleted successfully.");
            return ResponseEntity.ok(response);
        } catch (SchedulerException e) {
            response.put("status", "error");
            response.put("message", "Failed to delete job: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }

    @PostMapping("/pause/{jobName}")
    public ResponseEntity<Map<String, Object>> pauseJob(@PathVariable String jobName) {
        Map<String, Object> response = new HashMap<>();
        try {
            jobService.pauseJob(jobName);
            response.put("status", "success");
            response.put("message", "Order cleanup job '" + jobName + "' paused successfully.");
            return ResponseEntity.ok(response);
        } catch (SchedulerException e) {
            response.put("status", "error");
            response.put("message", "Failed to pause job: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }

    @PostMapping("/resume/{jobName}")
    public ResponseEntity<Map<String, Object>> resumeJob(@PathVariable String jobName) {
        Map<String, Object> response = new HashMap<>();
        try {
            jobService.resumeJob(jobName);
            response.put("status", "success");
            response.put("message", "Order cleanup job '" + jobName + "' resumed successfully.");
            return ResponseEntity.ok(response);
        } catch (SchedulerException e) {
            response.put("status", "error");
            response.put("message", "Failed to resume job: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }
}

Application

package com.example.quartzorder;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class QuartzOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuartzOrderApplication.class, args);
    }

}


責任編輯:武曉燕 來源: Java知識日歷
相關推薦

2024-08-27 13:43:38

Spring系統業務

2025-07-31 08:58:50

2025-03-28 09:15:50

2025-07-10 08:46:21

ConnectSpringBoot數據

2025-03-26 08:43:17

2024-02-26 08:50:37

訂單自動取消消息

2025-02-14 09:07:35

2025-05-20 09:00:04

SpringGeoHash派單

2025-07-22 03:15:00

SpringFlight零拷貝

2025-03-19 08:36:55

2025-04-23 08:50:00

SpringBootCurator分布式鎖

2023-10-09 16:35:19

方案Spring支付

2025-03-04 08:40:28

2025-06-17 08:39:43

2025-02-28 08:40:28

ZooKeeperSpringBoot計費系統

2025-04-08 08:50:37

SpringCamel系統

2025-03-31 08:43:34

SpringTika優化

2025-03-03 07:30:00

SpringBootJGraphT網絡建模

2025-05-09 08:34:57

RSocketSpringBoot聊天系統

2025-05-06 08:40:21

SpringPostGIS系統
點贊
收藏

51CTO技術棧公眾號

在线xxxxx| 懂色av一区二区三区四区五区| 久久精品www| 欧美性生活一级片| 欧洲色大大久久| 看一级黄色录像| 日韩av成人| 久久av中文字幕片| 91高清在线免费观看| 国产调教在线观看| 激情小说一区| 91精品婷婷国产综合久久 | 一本久道久久久| 色视频www在线播放国产成人| 国产精久久久久| 成人国产激情| 红桃视频成人在线观看| 咪咪色在线视频| 国产午夜在线观看| 成人国产一区二区三区精品| 国产精品免费一区豆花| 在线免费观看毛片| 香蕉久久网站| 国产亚洲精品成人av久久ww| 国产精品久久久久久亚洲色| 欧美日韩国产一区二区在线观看| 日本国产一区二区| 欧美性久久久久| 丁香花电影在线观看完整版| 中文字幕亚洲在| 青青成人在线| 五月婷婷在线观看视频| 丰满亚洲少妇av| 成人福利视频网| 青青艹在线观看| 老鸭窝91久久精品色噜噜导演| 欧美成人激情图片网| 国产一区二区三区视频播放| 视频一区在线观看| 亚洲精品久久久久久久久| 岛国大片在线免费观看| 国产亚洲观看| 91精品国产综合久久精品| 亚洲色图久久久| 超碰这里只有精品| 欧美网站大全在线观看| 中文字幕一区二区三区四区在线视频| 国产三级电影在线播放| 午夜久久福利影院| av免费观看国产| 9999在线视频| 精品欧美激情精品一区| 成年人午夜视频在线观看| 成人影音在线| 天天色综合成人网| 久久久久狠狠高潮亚洲精品| 日韩激情电影免费看| 黑人巨大精品欧美一区免费视频| 黄色片网址在线观看| 一个人看的www视频在线免费观看| 亚洲成人一区二区| 日韩欧美亚洲天堂| 亚洲优女在线| 欧美日韩国产一级片| 久久久久久久久久毛片| 亚洲三级av| 亚洲国产日韩欧美在线99| 超碰男人的天堂| 久久99高清| 中文字幕久久久| 一级片一级片一级片| 欧美1级日本1级| 午夜精品一区二区三区av| 免费在线不卡视频| 日本亚洲最大的色成网站www| 国产精品旅馆在线| 亚洲va天堂va欧美ⅴa在线| 成人avav在线| 日韩区国产区| 中文字幕有码在线观看| 无码av免费一区二区三区试看| 男女高潮又爽又黄又无遮挡| 丁香婷婷久久| 精品国产乱码久久久久久1区2区 | www午夜视频| 欧美大片91| 日韩av网址在线观看| 免费在线观看a视频| 91成人看片| 欧美一级免费看| 国产又黄又猛又爽| av亚洲精华国产精华| 亚洲国产欧洲综合997久久 | 成人3d精品动漫精品一二三| 美日韩精品免费视频| 日韩色图在线观看| 国产一区二区视频在线播放| 蜜桃91精品入口| 国产三级在线播放| 日本精品视频一区二区| 成人免费播放视频| 欧美日韩一二| 性日韩欧美在线视频| 国产精品欧美激情在线| 91丨九色丨国产丨porny| 国产精品美女在线播放| 伊人久久综合一区二区| 欧美一级二级三级乱码| 亚洲一区二区自偷自拍| 亚洲日本黄色| 91在线免费观看网站| 蜜桃免费在线| 亚洲国产日韩在线一区模特| 国产美女视频免费看| 少妇精品导航| 久久久久久久激情视频| 国产精品久久影视| 国产三级一区二区三区| 热99这里只有精品| 影音先锋欧美激情| 久久在线精品视频| 影音先锋国产资源| 久久久91精品国产一区二区精品| 天天做天天躁天天躁| 日韩欧国产精品一区综合无码| 国产视频亚洲精品| 国产又大又黑又粗免费视频| 国产剧情av麻豆香蕉精品| 亚洲精品中文字幕乱码三区不卡| 自由日本语热亚洲人| 精品国产91九色蝌蚪| avove在线播放| 久草中文综合在线| 亚洲欧洲精品在线观看| 巨胸喷奶水www久久久免费动漫| 国产视频精品免费播放| 久久99精品波多结衣一区| jiyouzz国产精品久久| 拔插拔插海外华人免费| 国产成人福利av| 久久青草精品视频免费观看| 乱精品一区字幕二区| 亚洲综合偷拍欧美一区色| 久久精品无码一区二区三区毛片 | 国产一区二区三区小说| 亚洲成人偷拍| 欧美精品videos| 丰满熟妇乱又伦| 亚洲成精国产精品女| 国产成人av无码精品| aa级大片欧美三级| 久久久久久久久久久久久久一区| 久草免费在线视频| 亚洲欧美日韩中文视频| 国产一区免费看| 欧美国产精品v| 国产永久免费网站| 在线精品小视频| 国产精品大全| 一区二区电影免费观看| 一色桃子一区二区| 91无套直看片红桃| 亚洲欧美另类小说| 国产精品久久久久久久无码| 亚洲中午字幕| 亚洲精品二区| 日本一区二区三区电影免费观看 | 99riav视频一区二区| 日韩在线国产精品| 国产wwwwwww| 天天色图综合网| 91禁男男在线观看| 国产精品一级黄| 欧美日韩在线一| 日韩精品首页| 国产传媒欧美日韩| 欧美大电影免费观看| 日韩在线一区二区三区免费视频| 99久久久久久久| 午夜成人免费视频| 天堂在线中文视频| 国产精品乡下勾搭老头1| av高清在线免费观看| 久久国产电影| 亚洲自拍偷拍色片视频| 性爽视频在线| 欧美成人四级hd版| 青青草免费在线| 欧美一区在线视频| 日韩电影在线观看一区二区| 亚洲精品第1页| 亚洲熟妇一区二区三区| 卡一卡二国产精品 | 久久99精品国产91久久来源| 欧美久久在线观看| 欧美日韩一二三四| 国产成人成网站在线播放青青| 日本一区二区电影| 久久久久久久久国产精品| av色图一区| 亚洲国产精品推荐| 999久久久久久| 色婷婷精品大在线视频| 国产亚洲欧美精品久久久www| 国产欧美综合色| 国产视频久久久久久| 久久99精品国产麻豆婷婷 | 三上悠亚激情av一区二区三区| 操日韩av在线电影| 成年在线电影| 亚洲欧美日韩国产成人| www.爱爱.com| 欧美日韩成人高清| 蜜臀精品一区二区三区| 亚洲丰满少妇videoshd| 亚洲一级生活片| 中文字幕成人在线观看| 日韩一级视频在线观看| 国产毛片精品视频| 做a视频在线观看| 久久精品九九| 国产素人在线观看| 国产精品www994| 熟妇熟女乱妇乱女网站| 欧美xxxx中国| 亚洲视频精品一区| 欧美视频网址| 日本一区不卡| 国产精品三级| 欧美日韩视频在线一区二区观看视频 | 韩国av一区| www.99riav| 一区二区三区在线电影| 特级黄色录像片| 国产精品久久久久久麻豆一区软件 | 一区二区在线免费观看视频| 黄色资源网久久资源365| 五月婷婷激情久久| 日本欧美韩国一区三区| 欧美伦理片在线看| 日韩vs国产vs欧美| 亚洲不卡视频在线| 免费观看久久久4p| 亚洲高清免费在线观看| 久草精品在线观看| 国产亚洲视频一区| 国产一区二区三区久久悠悠色av| www.污污视频| 国产精品一区二区在线观看网站 | 一级特黄aaaaaa大片| 精品视频在线免费| 一区二区三区播放| 91精品国产入口| 国产高清免费av| 日韩精品一区二区三区四区视频 | 国产丝袜在线精品| 免费看污片的网站| 国产精品嫩草久久久久| 99久久久免费精品| 一区二区三区不卡视频| 国产福利拍拍拍| 色久优优欧美色久优优| 中文字幕91爱爱| 日韩一区二区三区在线| 免费观看a视频| 亚洲欧美日韩第一区| 日韩理伦片在线| 水野朝阳av一区二区三区| 中文一区二区三区四区| 成人在线中文字幕| 欧美.com| 国精产品一区二区| 国产探花一区在线观看| 中文字幕日韩精品一区二区| 欧美日韩亚洲三区| 日本wwww视频| 另类小说综合欧美亚洲| 日本天堂在线播放| 久久久亚洲精品一区二区三区| 日本精品久久久久中文| 一区二区三区美女视频| 久久精品视频7| 欧美丰满高潮xxxx喷水动漫| 黄色av免费观看| 日韩在线中文字幕| mm视频在线视频| 国产日韩欧美在线播放| 黄色美女久久久| 中文字幕成人一区| 国产精品人人爽人人做我的可爱 | 久久99精品一区二区三区| 少妇被狂c下部羞羞漫画| 日本一区二区三区四区在线视频| 69av.com| 欧美伊人久久久久久午夜久久久久| aa视频在线免费观看| 亚洲欧美在线x视频| dy888亚洲精品一区二区三区| 欧美最猛黑人xxxx黑人猛叫黄| 成人噜噜噜噜| 色播五月综合| 日韩午夜免费| 欧美性猛交xxxx乱大交91| 国产婷婷精品av在线| 五月天婷婷网站| 91精品免费在线观看| 懂色av中文在线| 91精品国产99久久久久久| 久久久91麻豆精品国产一区| 日本精品一区二区| 亚洲精品偷拍| 免费看三级黄色片| 中文字幕一区二区三区不卡 | 欧美精品一区二区精品网| 色欧美激情视频在线| 欧洲成人午夜免费大片| 一区二区三区视频免费视频观看网站| 色婷婷精品国产一区二区三区| 在线日本成人| 欧美熟妇另类久久久久久多毛| 欧美激情一区二区三区四区 | 先锋亚洲精品| 人妻换人妻a片爽麻豆| 亚洲欧美色图小说| 7777久久亚洲中文字幕| 伊人久久大香线蕉av一区二区| 日韩欧美精品一区二区三区| 国产精品久久九九| 欧美1区视频| 亚洲成人福利视频| 亚洲码国产岛国毛片在线| 国产女18毛片多18精品| 精品国模在线视频| 欧美成人福利| 伊人久久青草| 激情国产一区二区| 国产精品99久久久久久成人| 7777精品久久久大香线蕉| 欧美18hd| 亚洲最大av网站| 欧美日韩 国产精品| 风韵丰满熟妇啪啪区老熟熟女| 一区二区三区毛片| 亚洲国产www| 久久久免费观看视频| 国内精品麻豆美女在线播放视频| 2019日韩中文字幕mv| 99麻豆久久久国产精品免费优播| 日本熟妇乱子伦xxxx| 国产丝袜精品视频| 欧洲av不卡| 一本色道久久综合亚洲精品婷婷| 蜜臀av性久久久久蜜臀aⅴ流畅| 免费成人深夜蜜桃视频| 在线播放视频一区| 天堂va在线| 精品一区在线播放| 蜜桃伊人久久| 美国美女黄色片| 777奇米四色成人影色区| xvideos国产在线视频| 国产另类自拍| 午夜在线一区二区| 久久成人小视频| 欧美变态tickling挠脚心| 岛国av在线网站| 日本一区精品| 狠狠网亚洲精品| 亚洲国产精一区二区三区性色| 亚洲美女av电影| 亚洲成a人片777777久久| 成人午夜免费在线视频| 99re热这里只有精品免费视频| 无码人妻精品一区二区三区9厂 | 韩国成人精品a∨在线观看| 免费一级全黄少妇性色生活片| 日韩激情第一页| 黄色日韩网站| 国产又粗又猛又爽又黄的网站 | 日本成人三级电影网站| 精品制服美女丁香| 日韩成年人视频| 色小说视频一区| 老司机在线精品视频| 五月天亚洲视频| 亚洲妇女屁股眼交7| av网站在线免费观看| 国产麻豆乱码精品一区二区三区 | 欧美日韩亚洲国产精品| 国产激情在线免费观看| 日韩一区二区三区四区五区六区| 欧美一区久久久| 无码日本精品xxxxxxxxx| 国产亚洲制服色| 亚洲精品久久久久久动漫器材一区| 日本久久久a级免费| 国内精品福利| 欧美xxxooo| 亚洲欧美日韩国产中文| 99这里只有精品视频|