camunda-dmn

       决策模型和标记DMN(Decision Model and Notation),是一个商业决策管理标准。
       当前camunda的DMN引擎部分支持DMN1.3标准,包括决策表(Decision Tables),决策字面量表达式(Decision Literal Expressions),决策需求图(Decision Requirements Graphs)和足够友好表达语言FEEL(Friendly Enough Expression Language )

决策表(Decision Table):将决策逻辑指定为表
决策字面量表达式(Decision Literal Expression):将决策逻辑指定为表达式
决策需求图(Decision Requirements Graph):为相互依赖的决策建模
足够友好表达语言FEEL(Friendly Enough Expression Language):DMN的默认表达式语言

一、DMN决策表(DMN Decision Table):

       决策表表示一个在DMN1.3中能够被描述成一个表格的决策逻辑。它包含输入(inputs),输出(outputs)和规则(rules)
       决策表在xml中用一个decision元素中的decisionTable元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <decisionTable id="decisionTable">
    <!-- ... -->
    </decisionTable>
  </decision>
</definitions>
图1:决策表

       决策名称描述了这个决策表提供的决策逻辑。它被设置成一个name属性在decision元素上。

<decision id="dish" name="Dish">
  <decisionTable id="decisionTable">
  <!-- ... -->
  </decisionTable>
</decision>
1.决策表名称和编号(Decision Name / Decision Id):

       决策表名称(Decision Table Name)描述了这个决策表提供的决策逻辑。它被设置成一个name属性在decision元素上。

<decision id="dish" name="Dish">
  <decisionTable id="decisionTable">
  <!-- ... -->
  </decisionTable>
</decision>

       决策表编号(Decision Id)是技术上的标识。它设置在decision元素的id属性上。每个决策必须要有唯一的id,当在部署到camunda BPM平台上时。引擎会将id用作部署决策定义的决策key。

<decision id="dish" name="Dish">
  <decisionTable id="decisionTable">
  <!-- ... -->
  </decisionTable>
</decision>
图2:决策名称和编号
2.DMN决策表输入(DMN Decision Table Input):

       一个决策表有一个或者多个输入,也被叫做输入子句。输入子句定义了编号(id),标签(label),表达式(expression),决策表输入类型(type)

       一个input可以通过双击决策表各列的列头来进行编辑。

       在xml中输入子句用decisionTable内的input元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <decisionTable id="decisionTable">
      <input id="input1" label="Season">
        <inputExpression id="inputExpression1" typeRef="string">
          <text>season</text>
        </inputExpression>
      </input>
      <!-- ... -->
    </decisionTable>
  </decision>
</definitions>
图3:决策表输入
2.1.输入编号(Input Id):

输入编号是决策表input的唯一标识。

2.2.输入标签(Input Label):

输入标签是input的一个简短的描述。

2.3.输入表达式(Input Expression):

输入表达式指定输入子句的值如何生成。这是一个会被DMN引擎解析的表达式。它通常很简单并且引用一个执行期可见的变量引用。

2.4.输入类型定义(Input Type Definition):

当输入表达式被DMN引擎解析后,结果会被转换成指定的类型,支持的类型在用户指南中查看。注意:类型不是必须的,但是推荐要设置,因为它可以帮助我们明白可能的输入值,和提供类型安全的校验。

2.5.输入表达式语言(Input Expression Language):

表达式语言可以指定,输入表达式的语言,如果没有指定,则会使用definitions元素中的全局表达式语言。如果全局表达式语言也没有设置,则默认使用JUEL表达式语言。

2.6.输入变量名称(Input Variable Name):

当输入表达式被解析并返回解析的值时,值会被存储到一个变量中,这个变量的名称就是输入变量名称。这个变量名还能使用表达式。

3.决策表输出(DMN Decision Table Output):

       决策表可以有一个或者多个输出(outputs),也被叫做输出子句。输出子句定义了编号(id),标签(label),名称(name),决策表输出类型(type)。

       在xml中输入子句用decisionTable内的output元素表示。

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <decisionTable id="decisionTable">
      <!-- ... -->
      <output id="output1" label="Dish" name="desiredDish" typeRef="string" />
      <!-- ... -->
    </decisionTable>
  </decision>
</definitions>
图4:决策表输出
3.1.输出编号(Output Id):

输出编号是决策表输出的唯一编号,被camunda平台用来关联被执行的决策的历史输出。

3.2.输出标签(Output Label):

输出标签时一个输出的简短描述。输出标签不是必须的,但是推荐设置,因为他可以帮助我们理解决策。

3.3.输出名称(Output Name):

输出名称引用的是决策表执行结果的输出值。如果决策表有多个输出,那么所有的输出必须由唯一的名字。

3.4.输出类型定义(Output Type Definition):

当输出条目被DMN引擎执行后,将执行的结果转换成这个指定的类型。支持的类型见用户指南.类型不是必须的,但是推荐设置,因为它可以提供一个类型安全的输出值。
另外,这个类型可以用来将输出值转换成另外一个类型,例如将字符串类型的80%转换成浮点数,详细见用户定制数据类型

图5:决策表规则

4.决策表规则(DMN Decision Table Output):

       决策表可以有多个规则。每个规则包含多个输入和输出条目。输入条目是条件,输出条目是规则的结果。如果每一个输入条目都满足,那么这个规则就是满足的,那么决策结果就包含这个规则的输出结果
       在xml中,用decisionTable元素内的rule元素来表示一个规则:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <decisionTable id="decisionTable">
      <!-- ... -->
      <rule id="rule2-950612891-2">
        <inputEntry id="inputEntry21">
          <text>"Winter"</text>
        </inputEntry>
        <inputEntry id="inputEntry22">
          <text><![CDATA[<= 8]]></text>
        </inputEntry>
        <outputEntry id="outputEntry2">
          <text>"Roastbeef"</text>
        </outputEntry>
      </rule>
      <!-- ... -->
    </decisionTable>
  </decision>
</definitions>
4.1输入条目(条件)(Input Entry (Condition)):

一个规则可以有一个个或者多个输入条目,这个条目就是这个规则的条件。每一个输入条目在text元素中包含一个表达式,如果这个输入条目满足,则表达式会被解析成true。

<inputEntry id="inputEntry41">
  <text>"Spring"</text>
</inputEntry>
4.2空输入条目(Empty Input Entry):

如果某个输入条目对这个规则无关紧要,这个表达式是空的,那么这个表达式一直都是满足的,即被解析为true。

<inputEntry id="inputEntry41">
  <text/>
</inputEntry>

如果使用的是FEEL表达式语言,那么空输入条目用-表示,其他的表达式是空。

4.3输入条目的表达式语言(Expression Language of an Input Entry):

表达式语言可以用expressionLanguage属性指定。支持的表达式语言见用户指南,如果没有设置表达式语言,就会使用设置在definitions中的全局表达式语言。如果全局表达式语言也没有设置,则会使用默认的FEEL表达式语言。

4.4输出条目(结果)(Output Entry (Conclusion)):

一个规则可以有多个输出条目,这个条目是规则的结果。每一个输出条目的text元素中包含一个表达式。

<outputEntry id="outputEntry4">
  <text>"Steak"</text>
</outputEntry>
4.5空输出条目(Empty Output Entry):

如果输出条目是空,那么输出会被忽略,并且不是决策表结果的一部分

4.6输出条目表达式(Expression Language of an Output Entry):

见输入条目表达式

4.7描述(Description):

见输出条目描述

5.DMN命中策略(DMN Hit Policy):

       决策表的命中策略指定决策表的结果由哪些组成。
       命中策略设置在decisionTable元素的hitPolicy属性中。如果没有设置,则默认是UNIQUE命中策略。

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <decisionTable id="decisionTable" hitPolicy="RULE ORDER">
      <!-- .. -->
    </decisionTable>
  </decision>
</definitions>
5.1命中策略的角色(The Role of a Hit Policy):

命中策略指定决策表的多少个规则可以满足,和哪些满足的规则会被包含到决策表结果中。unique, any 和 first命中策略总是会返回一个最满足的一个规则。rule order 和 collect命中策略会返回多个满足的规则。

5.2唯一命中策略(Unique Hit Policy):

只有一个或者没有规则可以满足。决策表的结果包含满足规则的输出条目。

如果超过一个规则满足,那么就违反了唯一命中策略。

5.3任何一个命中策略(Any Hit Policy):

多个规则可以满足,然后所有满足的规则都必须有相同的输出。这个决策表的结果只会包含一个满足规则的输出。

如果多个规则满足,并且生成了不同的输出,这就违反了命中策略。


图6:请假决策

这是一个请假的决策表。如果申请这没有假期,或者申请者正在试用期,申请会被拒绝。否则申请通过。

5.4第一个命中策略(First Hit Policy):

多个规则可以满足。决策表结果只包含第一个满足规则的输出。


图7:广告决策

参见上面的广告决策表。关于根据用户的当前年纪决定哪个广告应该被展示。例如,用户当前是19岁。所有的结果都匹配,但是因为这个命中策略是第一个命中策略,所以Cars广告将会被展示。

5.5规则顺序命中策略(Rule Order Hit Policy):

多个规则可以满足。决策表包含所有满足规则的输出,且按照规则在决策表中的顺序排序。


图8:广告决策

再看上面关于广告的决策,假设我们有一19岁的用户。所有的规则都满足,所以所有的输出都会按照规则的顺序给到决策表结果。它可能可以用来指示所显示广告的优先级。

5.6集合命中策略(Collect Hit Policy):

多个结果满足。决策表以任意顺序包含所有满足条件的输出结果。


图9:广告决策

这个命中策略,输出列表是没有顺序的。所以广告可以是任意的,如果年纪是19岁。

另外,集合命中策略可以指定一个聚合器(aggregator)。如果指定了聚合器,决策表的结果只会包含一个输出条目。聚合器会从所有的满足规则的输入条目生成这个输出条目。注意,集合命中策略使用了聚合器,决策表只能有一个输出。

5.7集合命中策略的聚合器(Aggregators for Collect Hit Policy):

集合命中策略还可以指定一个聚合器:
       Collect(Sum):所有输出值的求和
       Collect(Min):所有输出值中,最小的一个
       Collect(Max):所有输出值中,最大的一个
       Collect(Count):输出值的个数的统计

5.8求和聚合器(SUM aggregator):

求和聚合器将所有满足的规则的输出求和


图10:薪水决策

这个展示的决策表可以用作对一个员工的奖金进行求和,例如这个员工已经在公司工作了3.5年了。所以会满足第一、二、三个规则,并且这个决策表的结果就是600,所有的输出的求和。

5.9最小值聚合器(MIN aggregator):

最小值聚合器可以用作返回所有满足规则的最小输出值。看下图的汽车保险的例子。一个汽车多年没有发生事故,保险费用应该减少。


图11:汽车保险决策

例如,如果输入值是3.5年,那么结果应该是98.83,尽管第一、二、三个规则都匹配,但是第三个规则有最小的输出值。

5.10最大值聚合器(MAX aggregator):

最大值聚合器可以被用作返回所有满足规则的最大的输出值。


图12:零花钱决策

这个决策表表示小孩子的零花钱的数量的决策。根据小孩子的年纪增加,零花钱的数量会增加。例如,输入一个9,第一、二个规则会匹配到,第二个规则的输出比第一个规则的输出大,所以这个决策的输出是5.一个9岁的小孩子会得到5块钱的零花钱。

5.11计数聚合器(COUNT aggregator):

计数聚合器会返回满足的条件的个数


图13:薪水决策

例如,上图的奖金决策表,如果使用的是计数聚合器,且输入值是4,第一、二、三个规则满足,所以,这个决策表的结果是3,也就是说4年后决策表的结果是3个工资奖金。

二、DMN决策文字表达式(DMN Decision Literal Expression):

图14:决策字面量表达式

一个决策文字表达式表示决策逻辑可以被描述成一个DMN1.3的表达式。它由一个文字表达式和一个变量组成

在xml中,一个决策文字表达式可以用decision元素内的literalExpression元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="season" name="Season">
    <variable name="season" typeRef="string" />
    <literalExpression>
      <text>calendar.getSeason(date)</text>
    </literalExpression>
  </decision>
</definitions>
1.1决策名称和决策编号(Decision Name / Decision Id):
图15:决策文字表达式名称和编号

名称描述了这个文字表达提供的决策逻辑。它设置在decision元素的name属性中:

<decision id="season" name="Season">
    <!-- ... -->
</decision>

这个编号是决策的技术编号,它设置在decision元素的id属性中。
在部署到camunda BPM平台时,每一个决策应该有单独的编号。在部署决策定义时,这个编号被引擎用作决策的key

<decision id="season" name="Season">
    <!-- ... -->
</decision>
1.2文字表达式(Literal Expression):

这个文字表达式指定了决策的值是如何生成的。这是一个会被DMN引擎执行的表达式。它可以被用作复杂的计算,去调用一个提供了决策逻辑的bean,或者组合输出需求决策的值

表达式被设置在xml中literalExpression元素内的text元素中。

<literalExpression>
  <text>calendar.getSeason(date)</text>
</literalExpression>
1.3文字表达式语言(Literal Expression Language):

可以通过literalExpression的expressionLanguage属性指定文字表达式的表达式语言。支持的表达式语言详见用户指南

<literalExpression expressionLanguage="groovy">
  <text>calendar.getSeason(date)</text>
</literalExpression>

如果没有设置表达式语言,会使用设置在definitions元素上的全局表达式语言。

<definitions id="definitions"
             name="definitions"
             xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
             expressionLanguage="groovy"
             namespace="http://camunda.org/schema/1.0/dmn">
  <!-- ... -->
</definitions>

如果全局表达式语言也没有设置,会使用默认的表达式语言JUEL,JUEL详见用户指南

1.4变量(Variable):

一个文字表达式必须由一个变量(variable),且指定了name属性和type属性作为决策的结果。变量声明在decision元素内的variable元素中。

<decision id="season" name="Season">
  <variable name="season" />
</decision>
1.5变量名称(Variable Name):

变量名称用来引用一个文字表达式的决策结果。它在xml中用name属性指定:

<variable name="season" />
1.6变量类型定义(Variable Type Definition):

在variable元素中,决策结果的类型可以用typeRef属性指定。在决策表达式被执行后,会转换结果成指定的类型。支持的类型详见用户指南

<variable name="season" typeRef="string" />

注意,类型不是强制要求的,但是推荐提供。应该类型可以提供类型安全的表达式结果。

三、决策需求图(Decision Requirements Graph):

决策需求图(DRG)为决策领域建模,展示相关的最重要的元素和它们的依赖关系。建模的元素是决策(decisions),输入数据(input data),和知识源(knowledge sources)。

DRG的可视化表示称为决策需求图(DRD)。


图16:决策需要图

在XML中,DRG由definitions元素表示。

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="dish" name="Dish">
    <!-- ... -->
  </decision>
  <decision id="beverages" name="Beverages">
    <!-- ... -->
  </decision>
</definitions>
1.1决策(Decision):

一个决策需求图可以有一个或者多个决策。决策有名字和编号。决策逻辑必须在决策表中或者决策表达式中。

决策在definitions元素中的decision元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dish" name="Desired Dish" namespace="party">
  <decision id="beverages" name="Beverages">
    <decisionTable id="decisionTable">
    <!-- ... -->
    </decisionTable>
  </decision>
</definitions>
1.2被依赖的决策(Required Decisions):

一个决策可以有一个或者多个它依赖的决策,例如上图饮料的决策依赖食物的决策

一个必要的决策由informationRequirement元素内的requiredDecision元素表示。它有一个href属性和一个以#开始的值,这个值是必要的决策的编号(依赖的决策的id)。

<decision id="beverages" name="Beverages">
  <informationRequirement>
      <requiredDecision href="#dish" />
  </informationRequirement>
  <!-- ... -->
</decision>
1.3输入数据(Input Data):

输入数据表示一个或者多个决策的输入信息。

注意:输入数据没有执行的语义,并且会被camunda DMN引擎忽略。

它用definitions元素内的inputData元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
  <inputData id="guestsWithChildren" name="Guests with children?" />
  
  <decision id="beverages" name="Beverages">
    <informationRequirement>
      <requiredInput href="#guestsWithChildren" />
    </informationRequirement>
    <!-- ... -->
</definitions>
1.4知识源(Knowledge Source):

知识源表示决策的权限。
注意:知识源没有执行的语义,并且会被camunda DMN引擎忽略。

它用definitions元素内的knowledgeSource元素表示:

<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
  <knowledgeSource id="cookbook" name="Men's Cookbook" />
  
  <decision id="dish" name="Dish">
    <authorityRequirement>
      <requiredDecision href="#cookbook" />
    </authorityRequirement>
    <!-- ... -->
</definitions>

三、足够友好的表达式语言(Friendly Enough Expression Language (FEEL)):

DMN提供了足够友好的表达式语言,它可以被用作执行决策表里的表达式

1.1 FEEL提供的数据类型(FEEL Data Types):

1.String:
       FEEL支持字符串,字符串必须被双引号包裹起来。字符串只支持equal比较操作
2.Numeric Types:
       FEEL支持数字类型,例如整数integer。在Camunda DMN引擎中可用的数字类型:
       integer
       long
       double
数字类型支持所有的比较操作和范围操作
3.Boolean:
       FEEL支持布尔值true和false。布尔值只支持equal比较操作
4.Date:
       FEEL支持时间类型。Camunda DMN引擎中执行以下类型的时间:
date and time
       要创建日期和时间值,必须将函数日期和时间与单个String参数一起使用。 该参数以yyyy-MM-dd'T'HH:mm:ss格式指定日期和时间。
Date支持所有的比较操作和范围

1.2 FEEL的语言元素(FEEL Language Elements):

Camunda DMN引擎支持FEEL输入条目。 输入条目中表达的FEEL术语是简单的一元测试。 这些简单的一元测试针对表达式测试输入值,如果满足测试,则返回true,否则返回false。 该表达式可以包含本节中描述的不同元素。

比较(Comparison):
FEEL的简单一元测试支持下面的比较操作。请注意,equals操作是空不是用=。另外,不等于操作不是!=,不等于操作要用逻辑非元素(negation)。

名称 操作 例子 描述
等于 "Steak" 测试输入值是否等于给定的值
小于 < <10 测试输入值是否小于给定的值
小于或等于 <= <=10 测试输入值是否小于等于给定的值
大于 > >10 测试输入值是否大于给定的值
大于或等于 >= >=10 测试输入值是否大于等于给定的值

范围(Range):
某些FEEL数据类型,例如数字和日期类型,可以测试是给定值是否属于某个范围。这个范围包括一个开始值和一个结束值。 还需指定开始和结束值是否包含在范围内。

开始 结束 例子 描述
包含 包含 [1..10] 测试输入值是否大于等于开始值并且小于等于结束值
不包含 包含 ]1..10] 或者 (1..10] 测试输入值是否大于开始值并且小于等于结束值
包含 不包含 [1..10[ 或者 [1..10) 测试输入值是否大于等于开始值并且小于结束值
不包含 不包含 ]1..10[ 或者 (1..10) 测试输入值是否大于开始值并且小于结束值

析取(Disjunction):
FEEL简单一元测试可以指定一个联合表达式。这个联合表达式可以是比较和范围。当至少一个表达式是true,测试的结果就是true。

例子:

  • 3,5,7:      测试输入值是否是3, 5或者7
  • <2,>10:        测试输入值是否小于2或者大于10
  • 10,[20..30]:       测试输入值是否是10,或者大于等于20且小于等于30
  • "Spareribs","Steak","Stew":        测试输入值是否是Spareribs, Steak或者Stew
  • date and time("2015-11-30T12:00:00"),date and time("2015-12-01T12:00:00"):        2015年11月30号12:00:00,或者2015年12月12:00:00
  • >customer.age,>21:       测试输入值是否大于变量customer的age或者大于21

逻辑非(Negation):
FEEL简单的一元测试可以与not函数否定。 这意味着,如果包含的表达式返回true,则测试将返回false。 请注意,仅允许一个否定运算符作为第一运算符,它可以包含一个析取运算符。

例子:

  • not("Steak"):        测试输入值是否不是Steak
  • not(>10):       测试输入值是否不大于10,这意味着输入值是小于等于10的
  • not(3,5,7):       测试输入值是否不是3, 5或者7
  • not([20..30]):       测试输入值是否不是大于等于20且小于等于30

指定名称(Qualified Names):
FEEL简单一元表达式测试可以通过指定名称访问变量和对象的属性

例子:

  • x: 测试输入值是否等于变量x的值
  • >= x: 测试输入值是否大于等于变量x的值
  • < customer.age: 测试输入值是否小于customer对象age属性的值

日期函数(Date Functions):
FEEL简单一元测试提供了函数去创建时间类型值。Camunda DMN引擎支持以下日期函数:

  • date and time("..."): 用字符格式yyyy-MM-dd'T'HH:mm:ss值创建一个时期时间值

例子:

  • date and time("2015-11-30T12:00:00"):        测试输入值是否是2015-11-30 12:00:00
  • [date and time("2015-11-30T12:00:00")..date and time("2015-12-01T12:00:00")]:        测试输入值是否在2015-11-30 12:00:00和2015-12-01 12:00:00之间
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345