從壹開始微服務 [ DDD ] 之九 ║從軍事故事中吞鸭,明白領(lǐng)域命令驗證(上)

烽煙

哈嘍大家周二好呀寺董,咱們又見面了,上周末掐指一算刻剥,距離 圣誕節(jié) 只有 5 周的時間了(如果你還不知道為啥我要提圣誕節(jié)這個時間點遮咖,可以看看我的第二系列開篇《之一 ║ D3模式設計初探 與 我的計劃書》),然后我簡單的思考了下這個DDD領(lǐng)域驅(qū)動設計還剩下的知識點造虏,現(xiàn)在已經(jīng)進入了第二部分御吞,就是領(lǐng)域命令和領(lǐng)域驅(qū)動這一塊麦箍,第三部分包括Identity驗證和.net core api等設計點,大概就是剩了這么多陶珠,預計應該能在圣誕節(jié)前完成挟裂。還有一個就是,之前的八篇文章背率,已經(jīng)比較完整的實現(xiàn)了普通框架的整體搭建话瞧,我也單獨的新建了一個 Git分支—— Framework8 嫩与,如果你不想用領(lǐng)域命令寝姿、領(lǐng)域事件、事件回溯這些東西划滋,僅僅就想要一個空的框架饵筑,一個包括 EFCore+Dtos+Automapper+IoC+Repository 的空框架(就比如我的第一個系列,就是一個普通的框架处坪,請不要再說是這是一個普通三層了根资,拜托??),你就可以直接用這個Framework8 ****分支即可同窘。

image

言歸正傳玄帕,上次咱們說到了創(chuàng)建新student的時候,提出來一個問題想邦,不知道大家是否還記得裤纹,這里再給大家說明一下,還是每篇一問丧没,希望能好好思考下鹰椒,或者是看看自己是如何設計的:

問題1:平時是如何進行表單驗證的(包括:判空、字符類型有效呕童、業(yè)務驗證:成人不能小于18歲漆际、金額不能小于0等)?

問題2:如果后來驗證變化了改怎么辦夺饲?(比如:手機號要支持全球奸汇,或者座機;亦或者退休年齡從60歲變成65歲往声;)

1擂找、JavaScript前端驗證即可,后端從來不進行驗證烁挟?(問題2:修改js)

2婴洼、后端驗證:直接在Controller中,通過寫很多判斷邏輯撼嗓,比如 If Else等柬采,而且CURD還需要寫很多重復的判斷方法欢唾?(問題2:每一個地方都需要仔細修改,額粉捻。)

3礁遣、后端驗證:寫一個統(tǒng)一的驗證類,或者驗證機制肩刃,比如一個公共類祟霍?甚至更高級的AOP切面驗證?(問題2:好像還是無法滿足每個領(lǐng)域特例)

4盈包、后端驗證:在DTO基礎(chǔ)上沸呐,基于領(lǐng)域命令,通過中介者Bus分發(fā)呢燥?(當然這個就是以后我要寫的)

其實說實話崭添,前三種我都用過,甚至現(xiàn)在偶爾也還是會用叛氨,畢竟很平常的用法呼渣,但是現(xiàn)在我感覺第四種真的很整潔,真正的把整體項目放到了領(lǐng)域中寞埠,一切以領(lǐng)域為核心了 屁置。這里我先把第四種的應用層 Service 方法簡單寫下,你就知道多么簡潔了仁连,具體的會在下面兩篇文章中說到:

 /// <summary>
 /// StudentAppService 添加新 Student /// </summary>
 public void Register(StudentViewModel studentViewModel)
 { //講視圖模型蓝角,轉(zhuǎn)換成命名模型
     var registerCommand = _mapper.Map<RegisterStudentCommand>(studentViewModel); //通過Mediator處理程序分發(fā)命令 //執(zhí)行順序:驗證 -> 通知 -> 注冊
 Bus.SendCommand(registerCommand);
 }

老張說:這兩天我在研究,啃書的時候怖糊,發(fā)現(xiàn)了這個DDD領(lǐng)域驅(qū)動的整體流程帅容,從前臺數(shù)據(jù)傳遞視圖模型 ,到Dto的命令模型伍伤,然后對其校驗的命令驗證模式并徘,最后還有總線分發(fā),然后就是異常通知等等扰魂,就像是一場軍事戰(zhàn)斗中的過程:

這里說的命令是動作的意思麦乞,是用戶發(fā)出的一個請求(從前臺向后端),當然你也可以理解是改領(lǐng)域模型下的命令動作(從內(nèi)到外)劝评,還記得我們說到的讀寫分離CQRS么姐直,就是Command。

每一個個小的戰(zhàn)役(領(lǐng)域模型)蒋畜,都會有自己戰(zhàn)場的一些信息和動作數(shù)據(jù)(視圖模型)声畏,當然這里有正常的消息,也有惡性攻擊或者不當?shù)牟僮鳎恳粋€動作執(zhí)行都是一個前鋒部隊(領(lǐng)域命令模型)插龄,先鋒部隊把這些數(shù)據(jù)打包愿棋,加上時間戳等標識,生成命令標簽均牢,這個時候通過總線指揮官(中介者)糠雨,交給參謀來處理數(shù)據(jù)命令(領(lǐng)域驗證),進行安全甄別徘跪,將正常的甘邀、正確的往下傳遞,傳給司令部(持久化)垮庐,如果是惡性的錯誤信息松邪,則通過通訊兵打包給前線(通知),每次前線執(zhí)行操作突硝,只需要看看是否有通訊兵是否有錯誤異常提醒测摔,如果沒有則證明執(zhí)行成功。

當然還有事件回溯和事件源解恰,我會在以后文章說明,不知道這個栗子是否合理浙于,如果大家看不懂也沒關(guān)系护盈,或者請下邊留言,我們一起討論討論羞酗。

更新

有的小伙伴腐宋,可能看本文或者其他的概念的時候,比較懵懂檀轨,我這里根據(jù)自己的理解胸竞,簡單畫了個草圖,當然等系列結(jié)束的時候参萄,還是有完整的卫枝,這里先來一個簡單的:

image

零、今天實現(xiàn)棕色的部分

image

一讹挎、領(lǐng)域命令Commands —— 領(lǐng)域模型的先鋒官

說到這個領(lǐng)域命令校赤,大家肯定不會陌生,或者說應該是在哪里見過筒溃,沒錯马篮!就是我們在上上一篇《七 ║項目第一次實現(xiàn) & CQRS初探》中,說到的讀寫分離 CQRS 中的C —— Commend命令怜奖,這里我簡單說下浑测,為什么叫先鋒官,我們把整個項目比作一個戰(zhàn)場的化歪玲,前端一直和后端進行交互 —— 表單提交迁央,這個時候怎顾,肯定就離不開查詢和命令,查詢這里暫時先不說漱贱,就說一下這個命令槐雾,前端的任何一個動作其實都是一個事件。

大家肯定知道從前臺DTO拿到的實體模型數(shù)據(jù)幅狮,肯定不能直接操作領(lǐng)域模型(當然現(xiàn)在我們是直接這么操作的募强,直接用的是視圖模型和領(lǐng)域模型進行交互操作,這個時候領(lǐng)域模型就起到了一個沖鋒陷陣的作用了崇摄,其實這種設計不符合DDD領(lǐng)域設計的思想擎值,因為領(lǐng)域模型是一切的核心,它應該是一個個司令部逐抑,不能參與到前線鸠儿,他會下發(fā)出一個個的命令模型去執(zhí)行),這個時候我們的命令模型就出現(xiàn)了厕氨,他充當著從前臺到后臺的先鋒官的作用进每,執(zhí)行一個個的命令指令,完成從視圖模型到領(lǐng)域模型的操作和數(shù)據(jù)的過度作用命斧。

然后再通過中介者模式田晚,通過事件總線,通過領(lǐng)域命令一一分發(fā)出去国葬,然后通過驗證贤徒,最后是實現(xiàn)(比如持久化等),然后將中間產(chǎn)生的錯誤信息汇四,或者通知信息接奈,再扔給了前臺,所以說通孽,領(lǐng)域命令就是一個先鋒官序宦,這里你也看到了,他是一個個先鋒官利虫,他的作用是起到引導的作用挨厚,是下達命令的作用,他是不負責具體的邏輯實現(xiàn)的糠惫,具體是為什么呢疫剃,先按下不表歪今。咱們先看看如何定義一個領(lǐng)域命令绊袋。

希望上邊的三段話大家可以幫忙想一想乏德,如果想通了,但是和我寫的不一樣搂漠,請一定要留言芝发!

1霞掺、創(chuàng)建命令抽象基類

在 Christ3D.Domain.Core 領(lǐng)域核心層中秘蛇,新建Commands文件夾,并該文件夾下創(chuàng)建抽象命令基類 Command碉克,這里可能有小伙伴會問凌唬,這個層的作用,我就簡單再說下漏麦,這個層的作用是為了定義核心的領(lǐng)域知識的客税,說人話就是很多基類,比如 Entity 是領(lǐng)域模型的基類撕贞,ValueObject 是值對象的基類更耻,這里的Command 是領(lǐng)域命令的基類,當然捏膨,你也可以把他放到領(lǐng)域?qū)又醒砭靡粋€ Base 文件夾來表示,這小問題就不要爭議了号涯。

namespace Christ3D.Domain.Core.Commands
{ /// <summary>
    /// 抽象命令基類 /// </summary>
    public abstract class Command 
    { //時間戳
        public DateTime Timestamp { get; private set; } //驗證結(jié)果目胡,需要引用FluentValidation
        public ValidationResult ValidationResult { get; set; } protected Command()
        {
            Timestamp = DateTime.Now;
        } //定義抽象方法,是否有效
        public abstract bool IsValid();
    }
}

思考:為什么要單單頂一個抽象方法 IsValid()诚隙;

2讶隐、定義 StudentCommand ,領(lǐng)域命令模型

上邊的領(lǐng)域基類建好以后久又,我們就需要給每一個領(lǐng)域模型,建立領(lǐng)域命令了效五,這里有一個小小的繞地消,你這個時候需要靜一靜,想一想畏妖,

1脉执、為什么每一個領(lǐng)域模型都需要一個命令模型。

2戒劫、為什么是一個抽象類半夷。

namespace Christ3D.Domain.Commands
{ 
/// <summary>
    /// 定義一個抽象的 Student 命令模型 /// 繼承 Command /// 這個模型主要作用就是用來創(chuàng)建命令動作的,不是用來實例化存數(shù)據(jù)的迅细,所以是一個抽象類 /// </summary>
    public abstract class StudentCommand : Command
    {
         public Guid Id { get; protected set; }//注意:set 都是 protected 的 
        public string Name { get; protected set; }
         public string Email { get; protected set; } 
        public DateTime BirthDate { get; protected set; } 
        public string Phone { get; protected set; }
    }
}

希望這個時候你已經(jīng)明白了上邊的兩個問題了巫橄,如果不是很明白,請再好好思考下茵典,如果已經(jīng)明白了湘换,請繼續(xù)往下走。

3、基于命令模型彩倚,創(chuàng)建各種動作指令

上邊的模型創(chuàng)造出來了筹我,咱們需要用它來實現(xiàn)各種動作命令了,比如 URD 操作帆离,肯定是沒有 C 查詢的蔬蕊。這里就重點說一下創(chuàng)建吧,剩下兩個都一樣哥谷。

namespace Christ3D.Domain.Commands
{ /// <summary>
    /// 注冊一個添加 Student 命令 /// 基礎(chǔ)抽象學生命令模型 /// </summary>
    public class RegisterStudentCommand : StudentCommand
    { // set 受保護岸夯,只能通過構(gòu)造函數(shù)方法賦值
        public RegisterStudentCommand(string name, string email, DateTime birthDate, string phone)
        {
            Name = name;
            Email = email;
            BirthDate = birthDate;
            Phone = phone;
        } // 重寫基類中的 是否有效 方法 // 主要是為了引入命令驗證 RegisterStudentCommandValidation。
        public override bool IsValid()
        {
            ValidationResult = new RegisterStudentCommandValidation().Validate(this);//注意:這個就是命令驗證呼巷,我們會在下邊實現(xiàn)它 return ValidationResult.IsValid;
        }
    }
}

這里你應該就能明白第一步的那個問題了吧:為什么要單單頂一個抽象方法 IsValid()囱修;

不僅僅是驗證當前命令模型是否有效(無效是指:數(shù)據(jù)有錯誤、驗證失敗等等)王悍,只有有效了才可以往下繼續(xù)走(比如持久化等 )破镰,還要獲取驗證失敗的情況下,收錄哪些錯誤信息压储,并返回到前臺鲜漩,這個就是

new RegisterStudentCommandValidation()

的作用。注意這里還沒有實現(xiàn)集惋,我們接下來就會實現(xiàn)它孕似。

添加學生命令寫完了,然后就是更新 UpdateStudentCommand 和 刪除 RemoveStudentCommand 了刮刑,這里就不多說了喉祭。

image

二、領(lǐng)域驗證Validations —— 領(lǐng)域模型的安保官

這里為啥要說是安保官(就是起的名字雷绢,要是不貼切可以留言)呢泛烙,因為這是從前臺 視圖模型 到 領(lǐng)域模型 的一個屏障,這個就不用解釋了翘紊,因為他就是一個驗證的作用蔽氨,當一個個命令執(zhí)行的時候,需要對數(shù)據(jù)進行處理帆疟,就好像前線先鋒部隊執(zhí)行一個個命令的時候鹉究,需要對一個個事件或者數(shù)據(jù)進行判斷,有些錯誤的踪宠,假的數(shù)據(jù)是不能傳達到領(lǐng)域模型中的自赔,而我們的先鋒官是不會處理這些的,他們只負責一個個命令的執(zhí)行殴蓬,驗證工作就交給了Validations匿级,而且是每一條命令都需要進行驗證蟋滴,這是肯定的。那如何創(chuàng)建基于命令的驗證Validations呢痘绎,請往下看津函。

1、基于StudentCommand 創(chuàng)建抽象驗證基類

在上邊的領(lǐng)域命令中孤页,我們定義一個公共的抽象命令基類尔苦,在驗證中,F(xiàn)luentValidation已經(jīng)為我們定義好了一個抽象基類 AbstractValidator行施,所以我們只需要繼承它就行允坚。

namespace Christ3D.Domain.Validations
{ /// <summary>
    /// 定義基于 StudentCommand 的抽象基類 StudentValidation /// 繼承 抽象類 AbstractValidator /// 注意需要引用 FluentValidation /// 注意這里的 T 是命令模型 /// </summary>
    /// <typeparam name="T">泛型類</typeparam>
    public abstract class StudentValidation<T> : AbstractValidator<T> where T : StudentCommand
    { //受保護方法,驗證Name
        protected void ValidateName()
        {
       //定義規(guī)則蛾号,c 就是當前 StudentCommand 類
            RuleFor(c => c.Name)
                .NotEmpty().WithMessage("姓名不能為空")//判斷不能為空稠项,如果為空則顯示Message
                .Length(2, 10).WithMessage("姓名在2~10個字符之間");//定義 Name 的長度
        } //驗證年齡
        protected void ValidateBirthDate()
        {
            RuleFor(c => c.BirthDate)
                .NotEmpty()
                .Must(HaveMinimumAge)//Must 表示必須滿足某一個條件,參數(shù)是一個bool類型的方法鲜结,更像是一個委托事件
                .WithMessage("學生應該14歲以上展运!");
        } //驗證郵箱
        protected void ValidateEmail()
        {
            RuleFor(c => c.Email)
                .NotEmpty()
                .EmailAddress();
        } //驗證手機號
        protected void ValidatePhone()
        {
            RuleFor(c => c.Phone)
                .NotEmpty()
                .Must(HavePhone)
                .WithMessage("手機號應該為11位!");
        } //驗證Guid
        protected void ValidateId()
        {
            RuleFor(c => c.Id)
                .NotEqual(Guid.Empty);
        } // 表達式
        protected static bool HaveMinimumAge(DateTime birthDate)
        {
           return birthDate <= DateTime.Now.AddYears(-14);
        } // 表達式
        protected static bool HavePhone(string phone)
        {
             return phone.Length == 11;
        }
    }
}

關(guān)于 FluentValidation 的使用精刷,這里就不多說了拗胜,大家可以自己使用,基本的也就是這么多了怒允,當然大家也可以自己寫一些復雜的運算埂软,這里要說的重點是,大家應該也已經(jīng)發(fā)現(xiàn)了纫事,每一個驗證方法都是獨立的勘畔,互不影響,就算是有一個出現(xiàn)錯誤(當然不是編譯錯誤)丽惶,也不會影響當前整個領(lǐng)域命令咖杂,也就等同于不影響當前事件操作,是不是和以前相比蚊夫,不僅方便而且安全性更高了。

這個時候我們定義了這個抽象的學生驗證基類懦尝,剩下的就是需要針對不同的知纷,每一個領(lǐng)域命令,設計領(lǐng)域驗證了陵霉。

2琅轧、實現(xiàn)各個領(lǐng)域命令模型的驗證操作

這里就簡單說一個添加學生的命令驗證,我們實現(xiàn) StudentValidation<RegisterStudentCommand> 踊挠,并初始化相應的命令乍桂,這里可以看到冲杀,我們可以很自由針對某一個命令,隨心隨意的設計不同的驗證睹酌,而且很好的進行管控权谁,比如以后我們不要對名字控制了,我們只需要去掉這個方法憋沿。亦或者我們以后不僅支持手機號旺芽,還支持座機,這里就可以簡單的增加一個即可辐啄。

namespace Christ3D.Domain.Validations
{ /// <summary>
    /// 添加學生命令模型驗證 /// 繼承 StudentValidation 基類 /// </summary>
    public class RegisterStudentCommandValidation : StudentValidation<RegisterStudentCommand> { public RegisterStudentCommandValidation()
        {
            ValidateName();//驗證姓名
            ValidateBirthDate();//驗證年齡
            ValidateEmail();//驗證郵箱
            ValidatePhone();//驗證手機號 //可以自定義增加新的驗證
 }
    }
}

說到了這里采章,相信你應該也命令了領(lǐng)域驅(qū)動的第一個小部分了,就是我們的每一個操作是如何生成命令并進行驗證的壶辜,那聰明的你一定會問了悯舟,我們?nèi)绾尾僮鬟@些領(lǐng)域命令呢,總得有一個驅(qū)動程序吧砸民,它們自己肯定是不會運行的抵怎,不錯!請繼續(xù)往下看阱洪。

image

三便贵、運籌命令模型 —— 誰會是指揮官?

上邊也說到了視圖模型轉(zhuǎn)成命令模型冗荸,然后在命令模型中進行驗證承璃,現(xiàn)在問題來了,到底是誰在運籌著這些命令蚌本,說人話就是盔粹,是誰在調(diào)用著這些命令,如果你能看懂我說到程癌,那就恭喜你舷嗡,如果不是很懂,也沒關(guān)系嵌莉,今天咱們先不說這個指揮官进萄,今天先說說,我們平時是怎么玩兒的锐峭。

1中鼠、在 Action 中調(diào)用我們的領(lǐng)域命令

[HttpPost]
[ValidateAntiForgeryToken] public ActionResult Create(StudentViewModel studentViewModel)
{ try {
        ViewBag.ErrorData = null; // 視圖模型驗證
        if (!ModelState.IsValid) return View(studentViewModel); //添加命令驗證,采用構(gòu)造函數(shù)方法實例
        RegisterStudentCommand registerStudentCommand = new RegisterStudentCommand(studentViewModel.Name, studentViewModel.Email, studentViewModel.BirthDate, studentViewModel.Phone); //如果命令無效沿癞,證明有錯誤
        if (!registerStudentCommand.IsValid())
        {
            List<string> errorInfo = new List<string>(); //獲取到錯誤援雇,請思考這個Result從哪里來的 
            foreach (var error in registerStudentCommand.ValidationResult.Errors)
            {
                errorInfo.Add(error.ErrorMessage);
            } //對錯誤進行記錄,還需要拋給前臺
            ViewBag.ErrorData = errorInfo; return View(studentViewModel);
        } // 執(zhí)行添加方法
         _studentAppService.Register(studentViewModel);
        ViewBag.Sucesso = "Student Registered!"; return View(studentViewModel);
    } catch (Exception e)
    { return View(e.Message);
    }
}

這個很好理解椎扬,就是普通的調(diào)用惫搏,這里有兩個問題具温,可以有助于大家是否真正理解:

1、new RegisterStudentCommand() 為什么是構(gòu)造函數(shù)實例筐赔?

2铣猩、ValidationResult.Errors 錯誤信息是從哪里得到的?

如果這兩個看懂了川陆,給自己一個攢??吧剂习。這個時候,我們就需要把信息拋給前臺了较沪,怎么進行展示呢鳞绕,這里我用的是自定義視圖組件,如果你會可以快速看一遍尸曼,如果沒有用過们何,請仔細看看。

2控轿、自定義局部視圖頁面

添加一個視圖組件類

在 Web 根目錄下新建文件夾 ViewComponents冤竹,然后添加視圖組件類 AlertsViewComponent.cs

namespace Christ3D.UI.Web.ViewComponents
{ public class AlertsViewComponent : ViewComponent
    { /// <summary>
        /// Alerts 視圖組件 /// 可以異步,也可以同步茬射,注意方法名稱鹦蠕,同步的時候是Invoke /// 我寫異步是為了為以后做準備 /// </summary>
        /// <returns></returns>
        public async Task<IViewComponentResult> InvokeAsync()
        { var notificacoes = await Task.Run(() => (List<string>)ViewBag.ErrorData); //遍歷錯誤信息,賦值給 ViewData.ModelState
            notificacoes?.ForEach(c => ViewData.ModelState.AddModelError(string.Empty, c)); return View();
        }
    }
}

每一個視圖組件一個類在抛,固定寫法钟病,這個其實就像mvc的controller。那我們還需要配置 view刚梭,如何配置呢肠阱,請往下看。

設計視圖頁面

這里我是手動創(chuàng)建朴读,不知道有沒有快捷鍵屹徘,有知道的請留言哈

在 Views -> Shared 文件夾下,新建 Components\alerts\Default.cshtml 文件

@if (ViewData.ModelState.ErrorCount > 0)
{ <div class="alert alert-danger">
        <button type="button" class="close" data-dismiss="alert">×</button>
        <h3 id="msgRetorno">Alert! Something went wrong:</h3>
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    </div> }

@if (!string.IsNullOrEmpty(ViewBag.Sucesso))
{ <div class="alert alert-success">
        <button type="button" class="close" data-dismiss="alert">×</button>
        <h3 id="msgRetorno">@ViewBag.Sucesso</h3>
    </div> }

在主頁面內(nèi)調(diào)用

這里有兩種辦法:

 @* 將經(jīng)典驗證摘要替換為自定義視圖組件作為標記助手 *@

 @*方式一(可用衅金,但不推薦) @await Component.InvokeAsync("Alerts")*@ <!-- 如果使用這個方法噪伊,請記得在_ViewImports.cshtml 中,導入@addTagHelper "*, Christ3D.UI.Web" -->
 <vc:alerts />

我個人推薦使用第二種方法氮唯,注意 alerts酥宴,是我們的視圖名稱。

image

如果你想了解更多關(guān)于自定義視圖組件的知識您觉,可以查看官網(wǎng)

1、https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1

3授滓、瀏覽效果

image

這個時候琳水,我們已經(jīng)把視圖模型肆糕,命令模型,命令驗證等連接在一起在孝,也實現(xiàn)了我們的目的诚啃,看似很正常,其實問題還有很多:

這個指揮官真的指揮的很好么私沮?

為何contrller中還是會存在業(yè)務邏輯始赎?

等等。仔燕。造垛。

四、鳴金...

眼看時間已經(jīng)很晚晰搀,今天就暫時寫到這里了五辽。

這個時候你一定會發(fā)現(xiàn),這種異常數(shù)據(jù)的寫法真的很不舒服外恕,我們設計DDD領(lǐng)域驅(qū)動設計杆逗,目的就是為了要以領(lǐng)域為核心,把業(yè)務邏輯分離出去鳞疲,這個雖然用到了領(lǐng)域命令罪郊,和命令驗證,咋看分離出去了尚洽,但是調(diào)用的時候悔橄,還是沒有把視圖模型和命令模型穿起來,而且細心的你應該也發(fā)現(xiàn)了翎朱,我們的Service方法中橄维,還是使用的領(lǐng)域模型,這個是不對的拴曲。那我們?nèi)绾尾拍馨岩晥D模型争舞,領(lǐng)域模型,驗證模型和命令模型穿起來呢澈灼,又是如何很好的把獲取錯誤信息從controller撥離出來呢竞川,請聽下回分解~

image

五、GitHub & Gitee

https://github.com/anjoy8/ChristDDD

https://gitee.com/laozhangIsPhi/ChristDDD

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叁熔,一起剝皮案震驚了整個濱河市委乌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荣回,老刑警劉巖遭贸,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異心软,居然都是意外死亡壕吹,警方通過查閱死者的電腦和手機著蛙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耳贬,“玉大人踏堡,你說我怎么就攤上這事≈渚ⅲ” “怎么了顷蟆?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腐魂。 經(jīng)常有香客問我帐偎,道長,這世上最難降的妖魔是什么挤渔? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任肮街,我火速辦了婚禮,結(jié)果婚禮上判导,老公的妹妹穿的比我還像新娘嫉父。我一直安慰自己,他們只是感情好眼刃,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布绕辖。 她就那樣靜靜地躺著,像睡著了一般擂红。 火紅的嫁衣襯著肌膚如雪仪际。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天昵骤,我揣著相機與錄音树碱,去河邊找鬼。 笑死变秦,一個胖子當著我的面吹牛成榜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹦玫,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼赎婚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了樱溉?” 一聲冷哼從身側(cè)響起挣输,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎福贞,沒想到半個月后撩嚼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年绢馍,在試婚紗的時候發(fā)現(xiàn)自己被綠了向瓷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡舰涌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出你稚,到底是詐尸還是另有隱情瓷耙,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布刁赖,位于F島的核電站搁痛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宇弛。R本人自食惡果不足惜鸡典,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枪芒。 院中可真熱鬧彻况,春花似錦、人聲如沸舅踪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抽碌。三九已至悍赢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間货徙,已是汗流浹背左权。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痴颊,地道東北人赏迟。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像祷舀,于是被迫代替她去往敵國和親瀑梗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容