<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      eShopOnContainers 知多少[8]:Ordering microservice

      1. 引言

      Ordering microservice(訂單微服務(wù))就是處理訂單的了,它與前面講到的幾個微服務(wù)相比要復(fù)雜的多。主要涉及以下業(yè)務(wù)邏輯:

      1. 訂單的創(chuàng)建、取消、支付、發(fā)貨
      2. 庫存的扣減

      2. 架構(gòu)模式

      簡化的CQRS和DDD微服務(wù)設(shè)計

      如上圖所示,該服務(wù)基于CQRS 和DDD來實現(xiàn)。

      項目結(jié)構(gòu)

      從項目結(jié)構(gòu)來看,主要包括7個項目:

      1. Ordering.API:應(yīng)用層
      2. Ordering.Domain:領(lǐng)域?qū)?/li>
      3. Ordering.Infrastructure:基礎(chǔ)設(shè)施層
      4. Ordering.BackgroundTasks:后臺任務(wù)
      5. Ordering.SignalrHub:基于Signalr的消息推送和實時通信
      6. Ordering.FunctionalTests:功能測試項目
      7. Ordering.UnitTests:單元測試項目

      從以上的項目定義來看,該微服務(wù)的設(shè)計并符合DDD經(jīng)典的四層架構(gòu)。

      Ordering.API對應(yīng)DDD中分層

      核心技術(shù)選型:

      1. ASP.NET Core Web API
      2. Entity Framework Core
      3. SQL Server
      4. Swashbuckle(可選)
      5. Autofac
      6. Eventbus
      7. MediatR
      8. SignalR
      9. Dapper
      10. Polly
      11. FluentValidator

      3. 簡明DDD

      領(lǐng)域驅(qū)動設(shè)計是一種方法論,用于解決軟件復(fù)雜度問題。它強調(diào)以領(lǐng)域為核心驅(qū)動設(shè)計。主要包括戰(zhàn)略和戰(zhàn)術(shù)設(shè)計兩大部分,其中戰(zhàn)略設(shè)計指導(dǎo)我們在宏觀層面對問題域進行識別和劃分,從而將大問題劃分為多個小問題,分而治之。而戰(zhàn)術(shù)設(shè)計從微觀層面指導(dǎo)我們?nèi)绾螌︻I(lǐng)域進行建模。
      DDD開發(fā)過程
      其中戰(zhàn)術(shù)設(shè)計了引入了很多核心要素,指導(dǎo)我們建模:

      1. 值對象(Value Object)
      2. 實體(Entity)
      3. 領(lǐng)域服務(wù)(Domain Service)
      4. 領(lǐng)域事件(Domain Event)
      5. 資源庫(Repository)
      6. 工廠(Factory)
      7. 聚合(Aggregate)
      8. 應(yīng)用服務(wù)(Application Service)
        戰(zhàn)術(shù)要素

      其中實體、值對象和領(lǐng)域服務(wù)用于表示領(lǐng)域模型,來實現(xiàn)領(lǐng)域邏輯。
      聚合用于封裝一到多個實體和值對象,確保業(yè)務(wù)完整性。
      領(lǐng)域事件來豐富領(lǐng)域?qū)ο笾g的交互。
      工廠、資源庫用于管理領(lǐng)域?qū)ο蟮纳芷凇?br> 應(yīng)用服務(wù)是用來表達用例和用戶故事。

      有了以上的戰(zhàn)術(shù)設(shè)計要素還不夠,如果它們糅合在一起,還是會很混亂,因此DDD再通過分層架構(gòu)來確保關(guān)注點分離,即將領(lǐng)域模型相關(guān)(實體、值對象、聚合、領(lǐng)域服務(wù)、領(lǐng)域事件)放到領(lǐng)域?qū)樱瑢①Y源庫、工廠放到基礎(chǔ)設(shè)施層,將應(yīng)用服務(wù)放到應(yīng)用層。以下就是DDD經(jīng)典的四層架構(gòu):
      DDD經(jīng)典四層架構(gòu)

      以上相關(guān)圖片來源于:張逸 · 領(lǐng)域驅(qū)動戰(zhàn)略設(shè)計實踐

      如果對訂單微服務(wù)應(yīng)用DDD,那么要摒棄傳統(tǒng)的面向數(shù)據(jù)庫建模的思想,轉(zhuǎn)向領(lǐng)域建模。該項目中主要定義了以下領(lǐng)域?qū)ο螅?/p>

      • Order:訂單
      • OrderItem:訂單項
      • OrderStatus:訂單狀態(tài)
      • Buyer:買家
      • Address:地址
      • PaymentMethod:支付方式
      • CardType:銀行卡片類型

      在該示例項目中,定義了兩個聚合:訂單聚合和買家聚合,其中Order和Buyer分屬兩個聚合根,其中訂單聚合通過持有買家聚合的唯一ID進行關(guān)聯(lián)。如下圖所示:
      訂單聚會和買家聚合

      我們依次來看其對實體、值對象、聚合、資源庫、領(lǐng)域事件的實現(xiàn)方式。

      4.1. 實體、值對象與聚合

      實體相關(guān)類圖

      實體與值對象最大的區(qū)別在于,實體有標識符可變,值對象不可變。為了保證領(lǐng)域的不變性,也就是更好的封裝,所有的屬性字段都設(shè)置為private set,集合都設(shè)置為只讀的,通過構(gòu)造函數(shù)進行初始化,通過暴露方法供外部調(diào)用修改。
      從類圖中我們可以看出,其主要定義了一個Entity抽象基類,所有的實體通過繼承Entity來實現(xiàn)命名約定。這里面有兩點需要說明:

      1. 通過Id屬性確保唯一標識符
      2. 重寫EqualsGetHashCode方法(hash值計算:this.Id.GetHashCode() ^ 31)
      3. 定義DomainEvents來存儲實體關(guān)聯(lián)的領(lǐng)域事件(領(lǐng)域事件的發(fā)生歸根結(jié)底是由于領(lǐng)域?qū)ο蟮臓顟B(tài)變化引起的,而領(lǐng)域?qū)ο骩實體、值對象和聚合])中值對象是不可變的,而聚合往往包含多個實體,所以將領(lǐng)域事件關(guān)聯(lián)在實體上最合適不過。)

      值對象相關(guān)類圖

      同樣,值對象也是通過繼承抽象基類ValueObject來進行約定。其主要也是重載了EqualsGetHashCode和方法。這里面有必要學(xué)習(xí)其GetHashCode的實現(xiàn)技巧:

      // ValueObject.cs
      protected abstract IEnumerable<object> GetAtomicValues();
      public override int GetHashCode()
      {
          return GetAtomicValues()
           .Select(x => x != null ? x.GetHashCode() : 0)
           .Aggregate((x, y) => x ^ y);
      }
      
      //Address.cs
      protected override IEnumerable<object> GetAtomicValues()
      {
          // Using a yield return statement to return each ele
          yield return Street;
          yield return City;
          yield return State;
          yield return Country;
          yield return ZipCode;
      }
      

      可以看到,通過在基類定義GetAtomicValues方法,用來要求子類指定需要hash的字段,然后將每個字段取hash值,然后通過異或運算再行聚合得到唯一hash值。

      所有對聚合中領(lǐng)域?qū)ο蟮牟僮鞫际峭ㄟ^聚合根來維護的。因此我們可以看到聚合根中定義了許多方法來處理領(lǐng)域邏輯。

      4.2. 倉儲

      倉儲相關(guān)類圖
      聚合中的領(lǐng)域?qū)ο蟮某志没柚鷤}儲來完成的。其提供統(tǒng)一的入口來進行聚合內(nèi)相關(guān)領(lǐng)域?qū)ο蟮腃RUD,從而完成透明持久化。從圖中看出,IRepository定義了一個IUnitOfWork屬性,其代表工作單元,主要定義了兩個方法SaveChangesAsyncSaveEntitiesAsync,借助事務(wù)一次性提交所有更改,以確保數(shù)據(jù)的完整性和有效性。

      4.3. 領(lǐng)域事件

      領(lǐng)域事件相關(guān)類圖

      從類圖中可以看出一個共同特征,都實現(xiàn)了INotification接口。對MediatR熟悉的肯定一眼就明白了。是的,這個是MediatR中定義的接口。借助MediatR,來實現(xiàn)事件處理管道。通過進程內(nèi)事件處理管道來驅(qū)動命令接收,并將它們(在內(nèi)存中)路由到正確的事件處理器。
      關(guān)于MeidatR可以參考我的這篇博文:MediatR 知多少

      而關(guān)于領(lǐng)域事件的處理,是通過繼承INotificationHanlder接口來實現(xiàn),這樣INotificationINotificationHandler通過Ioc容器的服務(wù)注冊,自動完成事件的訂閱。而領(lǐng)域事件的處理其下放到了Ordering.Api中處理了。這里大家可能會有疑惑,既然叫領(lǐng)域事件,那為什么領(lǐng)域事件的處理不放到領(lǐng)域?qū)幽兀课覀兛梢赃@樣理解,事件是領(lǐng)域內(nèi)觸發(fā),但對事件的處理,其并非都是業(yè)務(wù)邏輯的相關(guān)處理,比如訂單創(chuàng)建成功后發(fā)送短信、郵件等就不屬于業(yè)務(wù)邏輯。

      eShopOnContainers中領(lǐng)域事件的觸發(fā)時機并非是即時觸發(fā),選擇的是延遲觸發(fā)模式。具體的實現(xiàn),后面會講到。

      5. Ordering.Infrastructure:基礎(chǔ)設(shè)施層

      基礎(chǔ)設(shè)施層主要用于提供基礎(chǔ)服務(wù),主要是用來實體映射和持久化。

      Ordering.Infrastructure 代碼結(jié)構(gòu)

      從圖中可以看到,主要包含以下業(yè)務(wù)處理:

      1. 實體類型映射
      2. 冪等性控制器的實現(xiàn)
      3. 倉儲的具體實現(xiàn)
      4. 數(shù)據(jù)庫上下文的實現(xiàn)(UnitOfWork的實現(xiàn))
      5. 領(lǐng)域事件的批量派發(fā)

      這里著重下第2、4、5點的介紹。

      5.1. 冪等性控制器

      冪等性是指某個操作多次執(zhí)行但結(jié)果相同,換句話說,多次執(zhí)行操作而不改變結(jié)果。舉例來說:我們在寫預(yù)插腳本時,會添加條件判斷,當表中不存在數(shù)據(jù)時才將數(shù)據(jù)插入到表中。無論重復(fù)運行多少次 SQL 語句,結(jié)果一定是相同的,并且結(jié)果數(shù)據(jù)會包含在表中。

      那怎樣確保冪等性呢?一種方式就是確保操作本身的冪等性,比如可以創(chuàng)建一個表示“將產(chǎn)品價格設(shè)置為¥25”而不是“將產(chǎn)品價格增加¥5”的事件。此時可以安全地處理第一條消息,無論處理多少次結(jié)果都一樣,而第二個消息則完全不同。
      但是假設(shè)價格是一個時刻在變的,而你當前的操作就是要將產(chǎn)品價格增加¥5怎么辦呢?顯然這個操作是不能重復(fù)執(zhí)行的。那我如何確保當前的操作只執(zhí)行一次呢?
      一種簡便的方法就是記錄每次執(zhí)行的操作。該項目中的Idempotency文件夾就是來做這件事的。

      Idempotency 類圖

      從類圖來看很簡單,就是每次發(fā)送事件時生成一個唯一的Guid,然后構(gòu)造一個ClientRequest對象實例持久化到數(shù)據(jù)庫中,每次借助MediatR發(fā)送消息時都去檢測消息是否已經(jīng)發(fā)送。

      冪等性處理

      5.2. UnitOfWork(工作單元的實現(xiàn))

      Uow實現(xiàn)邏輯

      從代碼來看,主要干了兩件事:

      1. 在提交變更之前,觸發(fā)所有的領(lǐng)域事件
      2. 批量提交變更

      這里需要解釋的一點是,為什么要在持久化之前而不是之后進行領(lǐng)域事件的觸發(fā)呢?
      這種觸發(fā)就是延遲觸發(fā),將領(lǐng)域事件的發(fā)布與領(lǐng)域?qū)嶓w的持久化放到一個事務(wù)中來達到一致性。
      當然這有利有弊,弊端就是當領(lǐng)域事件的處理非常耗時,很有可能會導(dǎo)致事務(wù)超時,最終導(dǎo)致提交失敗。而避免這一問題,也只有做事務(wù)拆分,這時就要考慮最終一致性和相應(yīng)的補償措施,顯然更復(fù)雜。

      至此,我們可以總結(jié)下聚合、倉儲與數(shù)據(jù)庫之間的關(guān)系,如下圖所示。

      6. Ordering.Api:應(yīng)用層

      應(yīng)用層通過應(yīng)用服務(wù)接口來暴露系統(tǒng)的全部功能。在這里主要涉及到:

      1. 領(lǐng)域事件的處理
      2. 集成事件的處理
      3. CQRS的實現(xiàn)
      4. 服務(wù)注冊
      5. 認證授權(quán)
      6. 集成事件的訂閱

      項目結(jié)構(gòu)

      6.1. 領(lǐng)域事件和集成事件

      對于領(lǐng)域事件和集成事件的處理,我們需要先明白二者的區(qū)別。領(lǐng)域事件是發(fā)生在領(lǐng)域內(nèi)的通信(同步或異步均可),而集成事件是基于多個微服務(wù)(其他限界上下文)甚至外部系統(tǒng)或應(yīng)用間的異步通信。
      領(lǐng)域事件是借助于MediatR的INotification 和 INotificationHandler的接口來實現(xiàn)。

      其中Application/Behaviors文件夾中是實現(xiàn)MediatR中的IPipelineBehavior接口而定義的請求處理管道。

      集成事件的發(fā)布訂閱是借助事件總線來完成的,關(guān)于事件總線之前有文章詳述,這里不再贅述。在此,僅代碼舉例其訂閱方式。

      private void ConfigureEventBus(IApplicationBuilder app)
      {
          var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>();
      
          eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>();
      // some other code
      }
      

      6.2. 基于MediatR實現(xiàn)的CQRS

      CQRS(Command Query Responsibility Separation):命令查詢職責(zé)分離。是一種用來實現(xiàn)數(shù)據(jù)模型讀寫分離的架構(gòu)模式。顧名思義,分為兩大職責(zé):

      1. 命令職責(zé)
      2. 查詢職責(zé)

      其核心思想是:在客戶端就將數(shù)據(jù)的新增修改刪除等動作和查詢進行分離,前者稱為Command,通過Command Bus對領(lǐng)域模型進行操作,而查詢則從另外一條路徑直接對數(shù)據(jù)進行操作,比如報表輸出等。

      CQRS

      對于命令職責(zé),其是借助于MediatR充當?shù)腃ommandBus,使用IRequest來定義命令,使用IRequestHandler來定義命令處理程序。我們可以看下CancelOrderCommandCancelOrderCommandHandler的實現(xiàn)。

      public class CancelOrderCommand : IRequest<bool>
      {
      
          [DataMember]
          public int OrderNumber { get; private set; }
      
          public CancelOrderCommand(int orderNumber)
          {
              OrderNumber = orderNumber;
          }
      }
      
      public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, bool>
      {
          private readonly IOrderRepository _orderRepository;
      
          public CancelOrderCommandHandler(IOrderRepository orderRepository)
          {
              _orderRepository = orderRepository;
          }
      
          public async Task<bool> Handle(CancelOrderCommand command, CancellationToken cancellationToken)
          {
              var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
              if(orderToUpdate == null)
              {
                  return false;
              }
      
              orderToUpdate.SetCancelledStatus();
              return await _orderRepository.UnitOfWork.SaveEntitiesAsync();
          }
      }
      
      

      以上代碼中,有一點需要指出,就是所有Command中的屬性都定義為private set,通過構(gòu)造函數(shù)進行賦值,以確保Command的不變性。

      對于查詢職責(zé),通過定義查詢接口,借助Dapper直接寫SQL語句來完成對數(shù)據(jù)庫的直接讀取。
      查詢示例

      而對于定義的命令,為了確保每個命令的合法性,通過引入第三方Nuget包FluentValdiation來進行命令的合法性校驗。其代碼也很簡單,參考下圖。
      校驗器的定義和注冊

      6.3. 服務(wù)注冊

      整個訂單微服務(wù)中所有服務(wù)的注冊,都是放到應(yīng)用層來做的,在Ordering.Api\Infrastructure\AutofacModules文件夾下通過繼承Autofac.Module定義了兩個Module來進行服務(wù)注冊:

      • ApplicationModule:自定義接口相關(guān)服務(wù)的注冊
      • MediatorModule:Mediator相關(guān)接口服務(wù)的注冊

      將所有的服務(wù)注冊都放到高層模塊來進行注冊,有點違背關(guān)注點分離,各層應(yīng)該關(guān)注本層的服務(wù)注冊,所以這中實現(xiàn)方式是有待改進的。而具體如何改進,這里給大家提供一個線索,可參考ABP是如何實現(xiàn)進行服務(wù)注冊的分離和整合的。

      這里順帶提一下Autofac這個Ioc容器的一個限制,就是所有的服務(wù)注冊必須在程序啟動時完成注冊,不允許運行時動態(tài)注冊。

      7. Ordering.BackgroundTasks:后臺任務(wù)

      后臺任務(wù),顧名思義,后臺靜默運行的任務(wù),也稱計劃任務(wù)。在.NET Core 中,我們將這些類型的任務(wù)稱為托管服務(wù),因為它們是在主機/應(yīng)用程序/微服務(wù)中托管的服務(wù)/邏輯。請注意,這種情況下托管服務(wù)僅簡單表示具有后臺任務(wù)邏輯類。

      那我們?nèi)绾螌崿F(xiàn)托管服務(wù)了,一種簡單的方式就是使用.NET Core 2.0之后版本中提供了一個名為IHostedService的新接口。當然也可以選擇其他的一些后臺任務(wù)框架,比如HangFire、Quartz。

      該示例項目就是基于BackgroundService定義的一個后臺任務(wù)。該任務(wù)主要用于輪詢訂單表中處于已提交超過1分鐘的訂單,然后發(fā)布集成事件到事件總線,最終用來將訂單狀態(tài)更新為待核驗(庫存)狀態(tài)。

      public abstract class BackgroundService : IHostedService, IDisposable
      {
          protected BackgroundService();
      
          public virtual void Dispose();
          public virtual Task StartAsync(CancellationToken cancellationToken);
          [AsyncStateMachine(typeof(<StopAsync>d__4))]
          public virtual Task StopAsync(CancellationToken cancellationToken);
          protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
      }
      

      BackgroundService的方法申明中我們可以看出僅需實現(xiàn)ExecuteAsync方法即可。

      完成后臺任務(wù)的定義后,將服務(wù)注冊到Ioc容器中即可。

      public IServiceProvider ConfigureServices(IServiceCollection services)
      {
       //Other DI registrations;
       // Register Hosted Services
       services.AddSingleton<IHostedService, GracePeriodManagerService>();
       services.AddSingleton<IHostedService, MyHostedServiceB>();
       services.AddSingleton<IHostedService, MyHostedServiceC>();
       //...
      }
      

      總之,IHostedService 接口為 ASP.NET Core Web 應(yīng)用程序啟動后臺任務(wù)提供了一種便捷的方法。它的優(yōu)勢主要在于:當主機本身關(guān)閉時,可以利用取消令牌來優(yōu)雅的清理后臺任務(wù)。

      8. Ordering.SignalrHub:即時通信

      在訂單微服務(wù)中,當訂單狀態(tài)變更時,需要實時推送訂單狀態(tài)變更消息給客戶端。而這就涉及到實時通信。實時 HTTP 通信意味著,當數(shù)據(jù)可用時,服務(wù)端代碼會推送內(nèi)容到已連接的客戶端,而不是服務(wù)端等待客戶端來請求新數(shù)據(jù)。

      而對于實時通信,ASP.NET Core中SignalR可以滿足我們的需求,其支持幾種處理實時通信的技術(shù)以確保實時通信的可靠傳輸。

      該示例項目的實現(xiàn)思路很簡單:

      1. 訂閱訂單狀態(tài)變更相關(guān)的集成事件
      2. 繼承SignalR.Hub定義一個NotificationsHub
      3. 在集成事件處理程序中調(diào)用Hub進行消息的實時推送
      // 訂閱集成事件
      private void ConfigureEventBus(IApplicationBuilder app)
      {
          var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();  
          eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
          eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
          eventBus.Subscribe<OrderStatusChangedToStockConfirmedIntegrationEvent, OrderStatusChangedToStockConfirmedIntegrationEventHandler>();
          eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>();
          eventBus.Subscribe<OrderStatusChangedToCancelledIntegrationEvent, OrderStatusChangedToCancelledIntegrationEventHandler>();
          eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>();  
      }
      
      // 定義SignalR.Hub
      [Authorize]
      public class NotificationsHub : Hub
      {
      
          public override async Task OnConnectedAsync()
          {
              await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
              await base.OnConnectedAsync();
          }
      
          public override async Task OnDisconnectedAsync(Exception ex)
          {
              await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
              await base.OnDisconnectedAsync(ex);
          }
      }
      
      // 在集成事件處理器中調(diào)用Hub進行消息的實時推送
      public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>
      {
          private readonly IHubContext<NotificationsHub> _hubContext;
      
          public OrderStatusChangedToPaidIntegrationEventHandler(IHubContext<NotificationsHub> hubContext)
          {
              _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext));
          }
      
          public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event)
          {
              await _hubContext.Clients
                  .Group(@event.BuyerName)
                  .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus });
          }
      }
      
      

      8. 最后

      訂單微服務(wù)在整個eShopOnContainers中屬于最復(fù)雜的一個微服務(wù)了。
      通過對DDD的簡要介紹,以及對每一層的技術(shù)選型以及實現(xiàn)的思路和邏輯的梳理,希望對你有所幫助。

      posted @ 2019-01-24 09:02  「圣杰」  閱讀(5384)  評論(15)    收藏  舉報
      主站蜘蛛池模板: 久久久久无码中| 国产欧亚州美日韩综合区| 久久天天躁狠狠躁夜夜躁2012 | 国产成人8X人网站视频| 成人乱码一区二区三区av| 亚洲国产综合精品2020| 好吊视频一区二区三区在线| 免费无码午夜福利片| 精品无码成人片一区二区| 日韩精品福利视频在线观看| 日韩精品中文字幕人妻| 在线a久青草视频在线观看| 噜妇插内射精品| 精品视频不卡免费观看| av无码精品一区二区三区四区| 成人午夜污一区二区三区| 亚洲一区成人av在线| 云霄县| 国内精品久久久久久久coent| 少妇人妻偷人精品系列| 在线观看的网站| 亚洲综合av一区二区三区| 国产一区在线观看不卡| 欧美牲交videossexeso欧美| 国产av亚洲精品ai换脸电影| 国产精品一区二区性色av| 中国美女a级毛片| 成人网站免费观看永久视频下载| 男人的天堂va在线无码| 在线观看无码不卡av| 国产精品午夜福利精品| 自拍偷自拍亚洲一区二区| 久久婷婷成人综合色综合| 综合色一色综合久久网| 岛国最新亚洲伦理成人| 一色屋精品视频在线观看| 男女啪啪高清无遮挡免费| 国产无人区码一区二区| 庆城县| 一本无码在线观看| 国产福利微视频一区二区|