精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

正在播放精油久久| av成人app永久免费| 欧美日韩中文一区二区| 一区二区视频在线看| 亚洲高清一区二| 日韩一二三区不卡在线视频| 青娱乐免费在线视频| 电影一区二区三区| 成人美女视频在线观看18| 三级精品视频久久久久| 欧美精品久久久久久久久久久| 亚洲精品国产欧美在线观看| 国产精品1luya在线播放| 亚洲天堂网中文字| 国产精品久久av| 屁屁影院国产第一页| 在线观看wwwxxxx| 成人嫩草影院| 日韩欧美国产骚| 国产欧美韩日| 黄色一级片在线| 中文字幕伦av一区二区邻居| 欧美日韩亚洲网| 国产亚洲欧美一区二区| 成人免费一区二区三区| 伊人狠狠色j香婷婷综合| 日韩一区二区在线免费观看| 中文字幕免费高| 亚洲在线免费观看视频| 欧美大胆视频| 精品久久久久久电影| 精品乱子伦一区二区三区| 动漫精品一区一码二码三码四码| 日韩中文字幕| 亚洲一区二区av电影| 福利视频久久| 丰满少妇乱子伦精品看片| 久久资源综合| 色综合一个色综合亚洲| 欧美一区视久久| 无码人妻精品一区二区三区蜜桃91 | 九九热精彩视频| 久久密一区二区三区| 欧美男男青年gay1069videost | 午夜影院免费体验区| 欧美日一区二区三区在线观看国产免| 欧美一区二区三区免费大片| 777久久精品一区二区三区无码| 国产chinasex对白videos麻豆| 欧美96在线丨欧| 欧美大胆人体bbbb| 九色自拍视频在线观看| 动漫一区在线| 成人精品一区二区三区四区| 久久中文字幕国产| www男人天堂| 欧美少妇网站| 国产精品青草综合久久久久99| 国产精品一区二区3区| 午夜成人亚洲理伦片在线观看| 久久99成人| 亚洲成人福利片| 欧美日本韩国国产| 国产又大又粗又长| 99热精品在线观看| 中文字幕在线精品| 五月天丁香社区| 99ri日韩精品视频| 欧美精品一区二| 色综合色综合色综合色综合| 金瓶狂野欧美性猛交xxxx| 久久人人爽人人爽| 亚洲一区中文字幕在线观看| 天堂网av手机版| 99久久婷婷国产综合精品电影√| 精品捆绑美女sm三区| 久草福利视频在线| 亚洲91av| 欧美激情中文字幕一区二区| 电影午夜精品一区二区三区| 亚洲第一页在线观看| 日韩一区欧美二区| 久久久久久久香蕉网| 老司机福利在线观看| 精品精品国产毛片在线看| 亚洲成在人线av| 成年人在线观看av| 日韩第一区第二区| 精品国产91九色蝌蚪| 超碰男人的天堂| 狠狠综合久久av一区二区蜜桃| 精品捆绑美女sm三区| 自拍视频一区二区| 激情五月综合| 欧美美女15p| 欧美丰满美乳xxⅹ高潮www| 电影中文字幕一区二区| 欧洲激情一区二区| 欧美日韩亚洲一| 国产第一页在线| 亚洲精品中文字幕乱码三区| 五月天亚洲综合| 亚洲人成色777777精品音频| 国产清纯白嫩初高生在线观看91 | 亚洲精品久久久久avwww潮水| 99v久久综合狠狠综合久久| 91夜夜未满十八勿入爽爽影院| 无码人妻精品一区二区三区9厂| 另类调教123区| 国产精品va在线播放| 国产精品久久久久精| 99精品国产热久久91蜜凸| 亚洲一区二区三区免费看| 懂色av中文在线| 久久蜜桃av一区精品变态类天堂| 一区国产精品| 伊人影院蕉久影院在线播放| 在线免费av一区| 美女露出粉嫩尿囗让男人桶| 久久精品国产www456c0m| 97精品一区二区视频在线观看| 九九热视频精品| 免费观看在线综合色| 国产精品亚洲欧美导航| 色婷婷av一区二区三| av一区二区三区四区| 国产在线精品一区二区中文 | 伊人久久亚洲综合| 丝袜美腿高跟呻吟高潮一区| 欧美尤物巨大精品爽| 成年人免费看毛片| 国产一区二区中文字幕| 亚洲最大成人在线| 成人资源www网在线最新版| 国产精品入口麻豆原神| 亚洲午夜精品久久久久久人妖| 日韩精品一区二区三区中文| 日韩视频免费在线观看| 丰满少妇被猛烈进入一区二区| 91精品一区二区三区综合在线爱| 久久综合电影一区| а中文在线天堂| ww久久中文字幕| 日韩在线三级| 韩国成人动漫| 亚洲免费av片| 婷婷丁香综合网| 久久看片网站| 久久影院理伦片| 亚洲视频tv| 亚洲午夜精品网| 色哟哟免费视频| 九色丨蝌蚪丨成人| 欧美激情18p| 五月天婷婷导航| 国产精品自拍在线| 久久精品日产第一区二区三区| wwwxxx在线观看| 91黄色免费看| 免费黄视频在线观看| 五月激激激综合网色播| 日韩在线免费视频观看| 久久精品五月天| 国产欧美日韩三区| 久久99999| 国产精品1luya在线播放| 欧美精品国产精品日韩精品| 风流老熟女一区二区三区| 国产三级一区二区三区| 男女裸体影院高潮| 亚洲国产欧美国产第一区| 亚洲图片在区色| 麻豆chinese极品少妇| 高清成人免费视频| 亚洲精品永久www嫩草| 成人av影院在线观看| 欧美精品一区二区三区蜜桃 | 极品人妻一区二区| 在线播放日韩| 欧美国产视频在线观看| 国产精品xxx| 亚洲精品福利免费在线观看| 日本免费网站视频| 乱码第一页成人| 亚洲尤物视频网| 欧美韩日亚洲| 精品视频久久久久久久| 91麻豆免费视频网站| 久久看片网站| 一区二区三区欧美在线| 香蕉成人app| 欧美亚洲激情视频| 日本暖暖在线视频| 精品av综合导航| 亚洲天堂视频在线播放| 一区二区三区精品在线观看| 99视频在线视频| 在线观看免费一区二区| 国产日韩中文在线| 国产高清视频在线| 日韩欧美中文字幕一区| 日韩欧美在线观看免费| 亚洲欧美日韩成人高清在线一区| 一区二区三区少妇| 亚洲日本久久| 国产99在线免费| 日韩伦理在线| 久久成年人视频| 欧美成人综合在线| 日韩欧美在线观看| 国产极品国产极品| 国产日本一区二区| 69xxx免费视频| 久久福利资源站| 日本久久高清视频| 国产精品一级在线观看| 2019中文字幕免费视频| 顶级网黄在线播放| 亚洲午夜久久久久久久| 天堂av资源网| 日韩视频免费观看高清完整版在线观看 | 久久久成人精品视频| 在线观看日批视频| 福利视频第一区| 久久久精品人妻一区二区三区四| 国产精品77777| 中文字幕超清在线免费观看| 亚洲欧洲av| 国产传媒欧美日韩| www久久久| 欧美成人午夜剧场免费观看| 性欧美18一19性猛交| 亚洲国产成人av网| 五月天av网站| 国产精品污网站| 欧美专区第二页| 一区在线视频| 国产91porn| 欧洲亚洲视频| 岛国一区二区三区高清视频| 奇米一区二区| 亚洲自拍偷拍区| 91成人在线网站| 国产精品自拍视频| 成人精品动漫| 国产精品video| 日韩欧美看国产| 爱福利视频一区| 91美女视频在线| 日韩欧美国产小视频| 91av久久久| 亚洲一区影音先锋| 日韩黄色免费观看| av影院午夜一区| 日韩综合第一页| 美女高潮久久久| 一级片视频免费观看| 卡一卡二国产精品| 日韩中文字幕a| 精品在线播放免费| 亚洲天堂av一区二区三区| 亚洲国产专区| 人妻久久久一区二区三区| 狠狠色丁香婷婷综合影院| 日韩高清三级| 91影院成人| 超级碰在线观看| 日韩亚洲国产欧美| 黄色片久久久久| 欧美 亚欧 日韩视频在线| 成人性做爰片免费视频| 欧美日韩视频一区二区三区| 国产96在线 | 亚洲| 欧美一级一区| 91免费版看片| 亚洲国产国产亚洲一二三| 国模吧无码一区二区三区| 天堂蜜桃一区二区三区| 最新天堂在线视频| 亚洲国产日韩欧美一区二区三区| 久久久999视频| 男女激情视频一区| 一级黄色大片免费看| 97精品久久久久中文字幕 | 一区二区自拍偷拍| 日韩欧美国产一二三区| 亚洲欧美丝袜中文综合| 一区二区三区视频免费在线观看| 亚洲国产www| 日韩精品在线免费播放| 在线观看黄av| 欧美激情一级二级| 成人精品电影在线| 欧美精品少妇videofree| www.超碰在线| 午夜精品一区二区三区av| 成人性生活视频| 91免费人成网站在线观看18| 久久a级毛片毛片免费观看| 亚洲v国产v在线观看| 欧美午夜视频| 在线观看亚洲色图| 成人免费看视频| 黄大色黄女片18免费| 亚洲高清不卡在线观看| 在线免费观看av片| 亚洲国产欧美一区| 老司机午夜在线| 久久精品中文字幕| 国产精品视频一区二区三区经| 九色porny自拍视频在线播放| 国产成人一区二区三区电影| 色戒汤唯在线| 国产欧美一区二区三区久久| 青青草这里只有精品| 天堂av在线中文| 视频一区在线视频| 永久免费未满蜜桃| 亚洲欧美一区二区三区极速播放| 手机在线看片1024| 亚洲成人黄色在线观看| 黄色片网站在线| 国产精品电影网站| 欧美电影在线观看免费| 国产情侣第一页| 亚洲高清资源| 91小视频网站| 久久免费美女视频| 男人的天堂一区二区| 日韩久久久精品| 黄网站app在线观看| 国产精品热视频| 视频欧美精品| 亚洲自拍高清视频网站| 999精品色在线播放| 日韩福利视频在线| 国内精品久久久久影院薰衣草| 91视频免费入口| 中文字幕在线观看不卡视频| 国产天堂av在线| 欧美性三三影院| a级片免费观看| 日韩小视频在线观看| 成人久久网站| 亚洲一区二区在| 美洲天堂一区二卡三卡四卡视频| 国产精品密蕾丝袜| 亚洲精品日韩综合观看成人91| 中文字幕在线观看欧美| 在线日韩日本国产亚洲| 主播大秀视频在线观看一区二区| 欧美区高清在线| 日韩成人精品在线| 欧美老女人性生活视频| 亚洲尤物视频在线| www久久久com| 欧美激情综合色综合啪啪五月| **爰片久久毛片| 福利视频免费在线观看| 青青草成人在线观看| 伊人av在线播放| 一区二区三区免费在线观看| www.四虎在线观看| 久久免费在线观看| 日本综合久久| 亚洲精品成人自拍| 精品亚洲成a人在线观看| 性色av无码久久一区二区三区| 欧美一区二区三区在线观看视频| 人人澡人人添人人爽一区二区| 国产精品国模大尺度私拍| 国产欧美综合一区二区三区| 三级黄色片免费观看| 亚洲精品国产一区二区精华液| 亚洲国产av一区二区| 欧美中文字幕在线| 91成人福利| 久色视频在线播放| 无码人妻av一区二区三区波多野| 亚洲国产精品久久久久秋霞不卡| 僵尸再翻生在线观看| 日本亚洲欧洲精品| 韩国成人福利片在线播放| 久久一区二区三| 亚洲丝袜av一区| 免费h视频在线观看| 欧美资源一区| 极品少妇一区二区| 日操夜操天天操| 欧美一区二区三级| 草草在线视频| 天堂av一区二区| 国产成人精品三级| 亚洲欧美偷拍视频| 美女国内精品自产拍在线播放| 成人涩涩网站| xxxx一级片| 亚洲a一区二区| 黄频网站在线观看| 国产精品69av|