麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發(fā)設(shè)計 > 正文

MVC實戰(zhàn)起步(一):一個簡易框架的搭建

2019-11-14 13:47:33
字體:
供稿:網(wǎng)友

一:引言

這僅僅是一個新手寫給新手共同入門的博文!這是一個使用MVC,和一些主流框架(Autofac,Log4Net等)來完成的一個簡單的項目。和各位學習MVC的朋友們一起學習。

二:項目分層

image

如左圖所示,先建好文件夾,然后再來填充內(nèi)容。

一:Zero,MVC4.0項目

二:Domain: Abstract倉儲類接口,Concrete倉儲類實現(xiàn),Entities實體模型

三:Infrastructure:基礎(chǔ)設(shè)施

四:IOC:主要用于解耦倉儲類接口

 

 

 

 

三:Infrastructure層建設(shè)

首先,從最底層寫起:

第一步,寫一個操作數(shù)據(jù)庫的類

我這邊采用的是底層使用ADO.NET,通過泛型約束和反射來實現(xiàn)一個簡單的ORM

1:先在Conntion里面放一個SQLHelper類(找個自己熟悉的),這里就不做工廠了。怎么簡單怎么來,先讓東西跑起來

2:寫個屬于自己的ORM類:我們先來想想,如果不考慮存儲過程(返回集合,直接LINQ處理數(shù)據(jù)),我們需要什么東西來拼接SQL語句,首先,要有表名,然后主鍵,然后各個字段名稱,其他的先不考慮,現(xiàn)在寫一個IDateBase抽象類來約束實體類,往Infrastructure層的IBase文件夾里新建一個IDateBase抽象類,代碼如下:

namespace Zero.Infrastructure.IBase{    public abstract class IDataBase    {        public virtual string TableName { get; set; }        public virtual int ID { get; set; }    }}
abstract class IDataBase

這樣就解決了表名和主鍵名稱在用泛型的時候,取不到的問題了,但是字段名稱不行啊,每個表的字段都不一樣,所以最后還是要用到反射,寫一個特性來反射,在Infrastructure層的Attributes文件夾下面建立一個類DataFieldAttribute

代碼如下:

namespace Zero.Infrastructure.Attributes{    public class DataFieldAttribute : Attribute    {        PRivate string _FieldName;        public DataFieldAttribute(string fieldname)        {            this._FieldName = fieldname;        }        public string FieldName        {            get { return this._FieldName; }            set { this._FieldName = value; }        }    }}

 

完事具備,我們先寫一個實體類,然后開始寫ORM

實體里建立在Domain的Entities文件夾下面,記得Domain層要引用Infrastructure層

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Zero.Infrastructure.Attributes;using Zero.Infrastructure.IBase;namespace Zero.Domain.Entities{    public class User: IDataBase    {        public User()        {            TableName = "User";        }        private string _TableName;        public override string TableName        {            get            {                if (_TableName == null)                { return "User"; }                else { return _TableName; }            }            set { _TableName = value; }        }        public override int ID { get; set; }        [DataFieldAttribute("UserName")]        public string UserName { get; set; }    }}
View Code

 

代碼如上

現(xiàn)在開始寫自己的ORM類

在Infrastructure層的Conntion文件夾下面建立一個ZeroORM類,ORM需要實現(xiàn)的功能有:查詢,添加,更改,刪除,4個基本功能

public class ZeroORMwhere<T> where T : IBase.IDataBase{    public string SqlConnctionString { get; set; }    public SqlConnection conn { get; set; }    public SqlTransaction tran { get; set; }}

 

然后增加查詢方法:

/// <summary>    /// 獲得實體T所有數(shù)據(jù)    /// </summary>    /// <returns></returns>    public List<T> Select(T t)    {        List<T> list = new List<T>();        string sql = "select * from " + t.TableName + " WITH (NOLOCK) order by id desc";        DataSet ds = SqlHelper.ExecuteDataset(SqlConnctionString, CommandType.Text, sql);        for (int i = 0; i < ds.Tables[0].Rows.Count; i++)        {            list.Add(DataSetToEntity.DsToEntity<T>(ds, i));        }        return list;    }

 

DataSetToEntity這個類就是網(wǎng)上找的Dataset轉(zhuǎn)實體的方法,大家可以網(wǎng)上找下,最后返回一個List集合

然后是添加方法

/// <summary>       /// 插入新數(shù)據(jù)       /// </summary>       /// <param name="t">實體類</param>       /// <returns></returns>       public int Insert(T t)       {           try           {               Type mytype = t.GetType();               // 獲取類的所有公共屬性               System.Reflection.PropertyInfo[] pInfo = mytype.GetProperties();               string FieldName = "";//字段名稱               string Values = "";//               StringBuilder sql = new StringBuilder();               List<SqlParameter> paras = new List<SqlParameter>();//不定參集合,防注入               sql.Append("Insert into ");               sql.Append(mytype.Name);//數(shù)據(jù)庫表名,可以放t.TableName               sql.Append("(");               object[] objDataFieldAttribute = null;               foreach (System.Reflection.PropertyInfo pio in pInfo)               {                   objDataFieldAttribute = pio.GetCustomAttributes(typeof(DataFieldAttribute), false);                   if (objDataFieldAttribute != null && objDataFieldAttribute.Length > 0)                   {                       FieldName += ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";//給字段賦值                       Values += "@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";//給對應(yīng)字段的值賦值                       paras.Add(new SqlParameter("@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName, pio.GetValue(t, null)));//添加不定參                   }               }               FieldName = FieldName.TrimEnd(',');               Values = Values.TrimEnd(',');               sql.Append(FieldName);               sql.Append(") VAlUES (");               sql.Append(Values);               sql.Append(")");               int i = SqlHelper.ExecuteNonQuery(SqlConnctionString, CommandType.Text, sql.ToString(), paras.ToArray());               return i;           }           catch (Exception)           {               return -1;               throw;           }       }
View Code

 

還有修改和刪除方法,以及帶事務(wù)的方法,一起貼出來。

using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using System.Reflection;using System.Text;using Zero.Infrastructure.Attributes;using Zero.Infrastructure.Utilities;namespace Zero.Infrastructure.Conntion{    public class ZeroORM<T> where T : IBase.IDataBase    {        public string SqlConnctionString { get; set; }        public SqlConnection conn { get; set; }        public SqlTransaction tran { get; set; }        /// <summary>        /// 獲得實體T所有數(shù)據(jù)        /// </summary>        /// <returns></returns>        public List<T> Select(T t)        {            List<T> list = new List<T>();            string sql = "select * from [" + t.TableName + "] WITH (NOLOCK) order by id desc";  //出于性能考慮,不用反射來獲取表名            DataSet ds = SqlHelper.ExecuteDataset(SqlConnctionString, CommandType.Text, sql);            for (int i = 0; i < ds.Tables[0].Rows.Count; i++)            {                list.Add(DataSetToEntity.DsToEntity<T>(ds, i));            }            return list;        }        /// <summary>        /// 根據(jù)主鍵ID獲取數(shù)據(jù)(一條)        /// </summary>        /// <param name="id"></param>        /// <param name="TableName"></param>        /// <returns></returns>        public T SelectByID(int id, T t)        {            string sql = "select * from [" + t.TableName + "] WITH (NOLOCK) where ID=@ID"; //出于性能考慮,不用反射來獲取表名            DataSet ds = SqlHelper.ExecuteDataset(SqlConnctionString, CommandType.Text, sql, new SqlParameter("@ID", id));            t = DataSetToEntity.DsToEntity<T>(ds, 0);            PropertyInfo[] prop = t.GetType().GetProperties();            return t;        }         /// <summary>        /// 插入新數(shù)據(jù)        /// </summary>        /// <param name="t">實體類</param>        /// <returns></returns>        public int Insert(T t)        {            try            {                Type mytype = t.GetType();                // 獲取類的所有公共屬性                System.Reflection.PropertyInfo[] pInfo = mytype.GetProperties();                string FieldName = "";//字段名稱                string Values = "";//                StringBuilder sql = new StringBuilder();                List<SqlParameter> paras = new List<SqlParameter>();                sql.Append("Insert into [");                sql.Append(mytype.Name);//數(shù)據(jù)庫表名                sql.Append("](");                object[] objDataFieldAttribute = null;                foreach (System.Reflection.PropertyInfo pio in pInfo)                {                    objDataFieldAttribute = pio.GetCustomAttributes(typeof(DataFieldAttribute), false);                    if (objDataFieldAttribute != null && objDataFieldAttribute.Length > 0)                    {                        FieldName += ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                        Values += "@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                        paras.Add(new SqlParameter("@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName, pio.GetValue(t, null)));                    }                }                FieldName = FieldName.TrimEnd(',');                Values = Values.TrimEnd(',');                sql.Append(FieldName);                sql.Append(") VAlUES (");                sql.Append(Values);                sql.Append(")");                int i = SqlHelper.ExecuteNonQuery(SqlConnctionString, CommandType.Text, sql.ToString(), paras.ToArray());                return i;            }            catch (Exception)            {                return -1;                throw;            }        }        /// <summary>        /// 更新數(shù)據(jù)        /// </summary>        /// <param name="t">需更新的實體類</param>        /// <returns></returns>        public int Update(T t)        {            try            {                int i = 0;                int primarykey = t.ID;                T oldT = t;                Type mytype = t.GetType();                System.Reflection.PropertyInfo[] pInfo = mytype.GetProperties();                oldT = SelectByID(primarykey, oldT);//獲得原始值,為日志做準備                if (t != oldT)                {                    string SetValue = "";//字段名稱                    string Where = " where ID=@ID";//                    StringBuilder sql = new StringBuilder();                    List<SqlParameter> paras = new List<SqlParameter>();                    sql.Append("Update [");                    sql.Append(mytype.Name);                    sql.Append("] Set ");                    object[] objDataFieldAttribute = null;                    foreach (System.Reflection.PropertyInfo pio in pInfo)                    {                        objDataFieldAttribute = pio.GetCustomAttributes(typeof(DataFieldAttribute), false);                        if (objDataFieldAttribute != null && objDataFieldAttribute.Length > 0 && pio.GetValue(t, null).ToString() != pio.GetValue(oldT, null).ToString())                        {                            SetValue += ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + "=" + "@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                            paras.Add(new SqlParameter("@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName, pio.GetValue(t, null)));                        }                    }                    SetValue = SetValue.TrimEnd(',');                    sql.Append(SetValue);                    sql.Append(Where);                    paras.Add(new SqlParameter("@ID", primarykey));                    i = SqlHelper.ExecuteNonQuery(SqlConnctionString, CommandType.Text, sql.ToString(), paras.ToArray());                    return i;                }                else                {                    return -2;                }            }            catch (Exception)            {                return -1;                throw;            }        }        /// <summary>        /// 刪除數(shù)據(jù)        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public int Delete(T t)        {            int i = 0;            int primarykey = t.ID;            Type mytype = t.GetType();            string TableName = mytype.Name;            string Where = " where ID =@ID";            string sql = "DELETE FROM " + TableName + Where;            try            {                i = SqlHelper.ExecuteNonQuery(SqlConnctionString, CommandType.Text, sql, new SqlParameter("@ID", primarykey));                return i;            }            catch (Exception)            {                return -1;                throw;            }        }        /// <summary>        /// 開始事務(wù)        /// </summary>        /// <returns></returns>        public void BeginTran()        {            try            {                conn = new SqlConnection(SqlConnctionString);                conn.Open();                tran = conn.BeginTransaction();            }            catch (Exception)            {                tran.Rollback();                conn.Close();                throw;            }        }        /// <summary>        /// 帶事務(wù)的插入方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public int InsertByTran(T t)        {            try            {                Type mytype = t.GetType();                // 獲取類的所有公共屬性                System.Reflection.PropertyInfo[] pInfo = mytype.GetProperties();                string FieldName = "";//字段名稱                string Values = "";//                StringBuilder sql = new StringBuilder();                List<SqlParameter> paras = new List<SqlParameter>();                sql.Append("Insert into [");                sql.Append(mytype.Name);//數(shù)據(jù)庫表名                sql.Append("](");                object[] objDataFieldAttribute = null;                foreach (System.Reflection.PropertyInfo pio in pInfo)                {                    objDataFieldAttribute = pio.GetCustomAttributes(typeof(DataFieldAttribute), false);                    if (objDataFieldAttribute != null && objDataFieldAttribute.Length > 0)                    {                        FieldName += ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                        Values += "@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                        paras.Add(new SqlParameter("@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName, pio.GetValue(t, null)));                    }                }                FieldName = FieldName.TrimEnd(',');                Values = Values.TrimEnd(',');                sql.Append(FieldName);                sql.Append(") VAlUES (");                sql.Append(Values);                sql.Append(")");                int i = SqlHelper.ExecuteNonQuery(tran, CommandType.Text, sql.ToString(), paras.ToArray());                return i;            }            catch (Exception)            {                tran.Rollback();                return -1;                throw;            }        }        /// <summary>        /// 帶事務(wù)的更新方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public int UpdateByTran(T t)        {            try            {                int i = 0;                int primarykey = t.ID;                T oldT = t;                Type mytype = t.GetType();                System.Reflection.PropertyInfo[] pInfo = mytype.GetProperties();                oldT = SelectByID(primarykey, oldT);//獲得原始值,為日志做準備                if (t != oldT)                {                    string SetValue = "";//字段名稱                    string Where = " where ID=@ID";//                    StringBuilder sql = new StringBuilder();                    List<SqlParameter> paras = new List<SqlParameter>();                    sql.Append("Update [");                    sql.Append(mytype.Name);                    sql.Append("] Set ");                    object[] objDataFieldAttribute = null;                    foreach (System.Reflection.PropertyInfo pio in pInfo)                    {                        objDataFieldAttribute = pio.GetCustomAttributes(typeof(DataFieldAttribute), false);                        if (objDataFieldAttribute != null && objDataFieldAttribute.Length > 0 && pio.GetValue(t, null).ToString() != pio.GetValue(oldT, null).ToString())                        {                            SetValue += ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + "=" + "@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName + ",";                            paras.Add(new SqlParameter("@" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName, pio.GetValue(t, null)));                        }                    }                    SetValue = SetValue.TrimEnd(',');                    sql.Append(SetValue);                    sql.Append(Where);                    paras.Add(new SqlParameter("@ID", primarykey));                    i = SqlHelper.ExecuteNonQuery(tran, CommandType.Text, sql.ToString(), paras.ToArray());                    return i;                }                else                {                    tran.Rollback();                    return -2;                }            }            catch (Exception)            {                tran.Rollback();                return -1;                throw;            }        }        /// <summary>        /// 帶事務(wù)的刪除方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public int DeleteByTran(T t)        {            int i = 0;            int primarykey = t.ID;            Type mytype = t.GetType();            string TableName = mytype.Name;            string Where = "] where ID =@ID";            string sql = "DELETE FROM [" + TableName + Where;            try            {                i = SqlHelper.ExecuteNonQuery(tran, CommandType.Text, sql, new SqlParameter("@ID", primarykey));                return i;            }            catch (Exception)            {                tran.Rollback();                return -1;                throw;            }        }        /// <summary>        /// 帶事務(wù)和條件的刪除方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public int DeleteByTran(T t, string where)        {            if (where.IndexOf("1=1") > 0)            {                return -1;            }            int i = 0;            string Where = "] where " + where;            string sql = "DELETE FROM [" + t.TableName + Where;            try            {                i = SqlHelper.ExecuteNonQuery(tran, CommandType.Text, sql, null);                return i;            }            catch (Exception)            {                tran.Rollback();                return -1;                throw;            }        }        /// <summary>        /// 提交事務(wù)        /// </summary>        /// <returns></returns>        public void CommitTran()        {            try            {                tran.Commit();                conn.Close();            }            catch (Exception)            {                tran.Rollback();                conn.Close();                throw;            }            finally            {                tran.Dispose();                conn.Dispose();            }        }        /// <summary>        /// 回滾事務(wù)        /// </summary>        public void RollBackTran()        {            try            {                tran.Rollback();            }            catch (Exception)            {                throw;            }        }    }}
View Code

 

好了,整個ORM就寫完了,這樣我們數(shù)據(jù)處理的類就寫完了!

三:Domain層

ORM寫完后,因為不同的表可能在不同的庫中,所以ZeroORM還不能直接拿來用,需要在上面隔離一層,我們這里選擇最簡單的方式,大牛勿噴。

首先寫一個Repository基類(這里就不抽象類了),代碼如下

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Zero.Domain.Abstract;using Zero.Infrastructure.IBase;namespace Zero.Domain.Concrete{    public class ConcreteBase<T> where T : IDataBase    {        public Zero.Infrastructure.Conntion.ZeroORM<T> DbHelper;        public ConcreteBase()        {            DbHelper = new Infrastructure.Conntion.ZeroORM<T>();        }        public string SqlConnctionString { get { return DbHelper.SqlConnctionString; } set { DbHelper.SqlConnctionString = value; } }        public List<T> GetAllList(T t)        {            List<T> ubiList = new List<T>();            ubiList = DbHelper.Select(t);            return ubiList;        }        public bool Insert(T t)        {            int i = 0;            i = DbHelper.Insert(t);            return i > 0;        }        public bool Update(T t)        {            int i = 0;            i = DbHelper.Update(t);            return i > 0;        }        public bool Delete(T t)        {            int i = 0;            i = DbHelper.Delete(t);            return i > 0;        }        /// <summary>        /// 帶事務(wù)的插入方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public bool InsertByTran(T t)        {            if (DbHelper.tran == null)            {                DbHelper.BeginTran();            }            int i = DbHelper.InsertByTran(t);            return i > 0;        }        /// <summary>        /// 帶事務(wù)的更新方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public bool UpdateByTran(T t)        {            if (DbHelper.tran == null)            {                DbHelper.BeginTran();            }            int i = DbHelper.UpdateByTran(t);            return i > 0;        }        /// <summary>        /// 帶事務(wù)的刪除方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public bool DeleteByTran(T t)        {            if (DbHelper.tran == null)            {                DbHelper.BeginTran();            }            int i = DbHelper.DeleteByTran(t);            return i > 0;        }        /// <summary>        /// 帶事務(wù)和條件的刪除方法        /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public bool DeleteByTran(T t, string where)        {            if (DbHelper.tran == null)            {                DbHelper.BeginTran();            }            int i = DbHelper.DeleteByTran(t, where);            return i > 0;        }         /// <summary>        ///         /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public void RollBackTran()        {            DbHelper.RollBackTran();        }        /// <summary>        ///         /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public void BeginTran()        {            DbHelper.BeginTran();        }        /// <summary>        /// 提交事務(wù) 提交成功返回"";失敗返回錯誤信息        /// </summary>        /// <returns>提交成功返回"";失敗返回錯誤信息</returns>        public string CommitTran()        {            if (DbHelper.tran != null)            {                try                {                    DbHelper.CommitTran();                    return "";                }                catch (Exception e)                {                    return e.ToString();                    throw;                }            }            else            {                return "不存在可提交的事務(wù)";            }        }        /// <summary>        /// 根據(jù)主鍵ID查詢結(jié)果,返回T        /// </summary>        /// <param name="id"></param>        /// <returns></returns>        public T SelectByID(string id, T t)        {            if (id != "")            {                return DbHelper.SelectByID(int.Parse(id), t);            }            else            {                return null;            }        }    }}
View Code

 

然后去寫Repository類和IRepository接口

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Zero.Domain.Entities;namespace Zero.Domain.Abstract{    public interface IUserRepository    {        User GetEntity();        IQueryable<User> Users { get; }    }}

 

實現(xiàn)類:

using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Text;using Zero.Domain.Abstract;using Zero.Domain.Entities;namespace Zero.Domain.Concrete{    public class UserRepository : ConcreteBase<User>, IUserRepository    {        public User GetEntity()        {            return new User();        }        public IQueryable<User> BaseTitleTypes        {            get { return GetAllList(GetEntity()).AsQueryable(); }        }        public UserRepository()            : base()        {            SqlConnctionString = ConfigurationManager.ConnectionStrings["ZeroTest"].ConnectionString;//獲取連接字符串        }    }}

 

然后去數(shù)據(jù)庫建表

image
鏈接字符串的位置寫的有點蠢,不過先這樣做吧,一切已快速為目的

現(xiàn)在,所有的準備工作都做完了,開始測試一下數(shù)據(jù)交互是否有問題!

在Zero項目中建一個Index控制器

image

image

然后右鍵Index添加視圖。先不做IOC解耦,引用Domain層和基礎(chǔ)設(shè)施層。

添加如下代碼

//  // GET: /Index/  public ActionResult Index()  {      UserRepository us = new UserRepository();      bool b =us.Insert(new Domain.Entities.User { UserName = "Ambre" });      if (b)      {          var ListEntity = from o in us.Users                           select o;          return Json(ListEntity, JsonRequestBehavior.AllowGet);      }      else      {          return Content(b.ToString());      }  }

 

然后去改下路由設(shè)置

image

啟動項目

image

好了,完成!下篇文章將寫如何快速的將IOC應(yīng)用到項目中,然后前端類似于EasyUi的grid控件如何編寫。

謝謝大家,喜歡的話,點個贊,這可是我的處女文呢!


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 免费国产一级淫片 | 欧洲精品视频在线观看 | 亚洲午夜激情网 | 92自拍视频 | 99re热视频这里只精品 | 国产一级免费在线视频 | 91看片淫黄大片欧美看国产片 | 欧美亚洲国产成人 | 九九热精品视频在线播放 | 毛片毛片 | 久久九九热re6这里有精品 | 国产成人精品无人区一区 | 一级免费观看 | 热99在线视频 | 视频一区二区不卡 | 伊久在线| 羞羞答答视频 | 久国产 | 青青草成人免费视频在线 | 亚洲午夜久久久久 | www国产成人免费观看视频,深夜成人网 | 亚洲午夜久久久久 | 黄在线| 国产分类视频 | 第一区免费在线观看 | 欧美a视频在线观看 | 中文字幕在线观看成人 | julieann艳星激情办公室 | 91色成人| 他也色在线视频 | 午夜久久久精品一区二区三区 | 香蕉在线播放 | 国产精品久久久久久久四虎电影 | 欧美日韩在线中文 | 99pron| 男女无套免费视频 | 国产成人精品一区二区三区电影 | 黄色av片三级三级三级免费看 | 成人做爰s片免费看网站 | 欧美一级高潮 | 在线观看一区二区三区四区 |