使用Activiti統(tǒng)一調(diào)度業(yè)務(wù)流程

activiti.jpg

什么是工作流/工作引擎?

Georgakopoulos給出的工作流定義是:工作流是將一組任務(wù)組織起來以完成某個(gè)經(jīng)營過程:定義了任務(wù)的觸發(fā)順序和觸發(fā)條件绸狐,每個(gè)任務(wù)可以由一個(gè)或多個(gè)軟件系統(tǒng)完成闻书,也可以由一個(gè)或一組人完成搏予,還可以由一個(gè)或多個(gè)人與軟件系統(tǒng)協(xié)作完罩息。它主要解決的是“使在多個(gè)參與者之間按照某種預(yù)定義的規(guī)則傳遞文檔、信息或任務(wù)的過程自動(dòng)進(jìn)行妈踊,從而實(shí)現(xiàn)某個(gè)預(yù)期的業(yè)務(wù)目標(biāo),或者促使此目標(biāo)的實(shí)現(xiàn)”泪漂。工作流管理系統(tǒng)的目標(biāo):管理工作的流程以確保工作在正確的時(shí)間被期望的人員所執(zhí)行——在自動(dòng)化進(jìn)行的業(yè)務(wù)過程中插入任何的執(zhí)行和干預(yù)廊营。所謂工作流引擎是指workflow作為應(yīng)用系統(tǒng)的一部分,并為之提供對(duì)各應(yīng)用系統(tǒng)有決定作用的根據(jù)角色萝勤、分工和條件的不同決定信息傳遞路由露筒、內(nèi)容等級(jí)等核心解決方案。例如開發(fā)一個(gè)系統(tǒng)最關(guān)鍵的部分不是系統(tǒng)的界面敌卓,也不是和數(shù)據(jù)庫之間的信息交換慎式,而是如何根據(jù)業(yè)務(wù)邏輯開發(fā)出符合實(shí)際需要的程序邏輯并確保其穩(wěn)定性、易維護(hù)性和彈性趟径。比如你的系統(tǒng)中有一個(gè)任務(wù)流程瘪吏,一般情況下這個(gè)任務(wù)的代碼邏輯、流程你都要自己來編寫蜗巧。實(shí)現(xiàn)它是沒有問題的掌眠。但是誰能保證邏輯編寫的毫無紕漏?經(jīng)過無數(shù)次的測(cè)試與改進(jìn)幕屹,這個(gè)流程沒有任何漏洞也是可以實(shí)現(xiàn)的蓝丙,但是明顯就會(huì)拖慢整個(gè)項(xiàng)目的進(jìn)度级遭。


BPMN2.0規(guī)范

BPMN(Business Process Model and Notation)--業(yè)務(wù)流程模型與符號(hào)。

BPMN是一套流程建模的標(biāo)準(zhǔn)渺尘,主要目標(biāo)是被所有業(yè)務(wù)用戶容易理解的符號(hào)挫鸽,支持從創(chuàng)建流程輪廓的業(yè)務(wù)分析到這些流程的最終實(shí)現(xiàn),知道最終用戶的管理監(jiān)控沧烈。

通俗一點(diǎn)其實(shí)就是一套規(guī)范掠兄,畫流程模型的規(guī)范。流程模型包括:流程圖锌雀、協(xié)作圖蚂夕、編排圖、會(huì)話圖腋逆。


什么是Activiti婿牍?

Activiti的創(chuàng)始人也就是JBPM(也是一個(gè)優(yōu)秀的BPM引擎)的創(chuàng)始人,從Jboss離職后開發(fā)了一個(gè)新的BPM引擎:Activiti惩歉。所以等脂,Activiti有很多地方都有JBPM的影子。Activiti是一個(gè)開源的工作流引擎撑蚌,它實(shí)現(xiàn)了BPMN 2.0規(guī)范上遥,可以發(fā)布設(shè)計(jì)好的流程定義,并通過api進(jìn)行流程調(diào)度争涌。Activiti 作為一個(gè)遵從 Apache 許可的工作流和業(yè)務(wù)流程管理開源平臺(tái)粉楚,其核心是基于 Java 的超快速、超穩(wěn)定的 BPMN2.0 流程引擎亮垫,強(qiáng)調(diào)流程服務(wù)的可嵌入性和可擴(kuò)展性模软,同時(shí)更加強(qiáng)調(diào)面向業(yè)務(wù)人員。Activiti 流程引擎重點(diǎn)關(guān)注在系統(tǒng)開發(fā)的易用性和輕量性上饮潦。每一項(xiàng) BPM 業(yè)務(wù)功能 Activiti 流程引擎都以服務(wù)的形式提供給開發(fā)人員燃异。通過使用這些服務(wù),開發(fā)人員能夠構(gòu)建出功能豐富继蜡、輕便且高效的 BPM 應(yīng)用程序回俐。ProcessEngine對(duì)象,這是Activiti工作的核心稀并。負(fù)責(zé)生成流程運(yùn)行時(shí)的各種實(shí)例及數(shù)據(jù)鲫剿、監(jiān)控和管理流程的運(yùn)行。所有的操作都是從獲取引擎開始的稻轨,所以一般會(huì)把引擎作為全局變量灵莲。

ProcessEngine processEngine = ProcessEngine.getDefaultProcessEngine();

為什么要用工作流引擎殴俱?

簡(jiǎn)單來說政冻,就是為了統(tǒng)一管理流程業(yè)務(wù)枚抵。想想看,如果要設(shè)計(jì)一個(gè)流程的程序明场,通常需要在數(shù)據(jù)庫中存各種狀態(tài)值汽摹,比如一個(gè)訂單程序,要標(biāo)記訂單是未付款苦锨、已付款逼泣、已出庫等等狀態(tài),而這些各種各樣的狀態(tài)參雜在程序中舟舒,邏輯自然就變得復(fù)雜了拉庶。而將這些狀態(tài)對(duì)應(yīng)流程里的一個(gè)個(gè)步驟,交由流程引擎去管理秃励,這樣不僅簡(jiǎn)化了業(yè)務(wù)邏輯代碼氏仗,而且,還有很強(qiáng)的擴(kuò)展性夺鲜。我可以修改我的流程皆尔,我可以添加一些步驟而不用改我的數(shù)據(jù)庫表結(jié)構(gòu),不用改我的業(yè)務(wù)邏輯币励。


在Eclipse中使用Activiti

推薦使用離線安裝:斷網(wǎng)

  1. 通過網(wǎng)盤:https://pan.baidu.com/s/15jZrf5LzlrgG82gPAh3cng 密碼:4kxd 慷蠕,下載在離線安裝包。

  2. 將下載好的jars文件夾里的3個(gè)jar文件復(fù)制到eclipse安裝目錄的plugins目錄下食呻。

  3. 刪除eclipse安裝目錄下流炕,configuration文件夾里的org.eclipse.update文件夾,重啟eclipse搁进。

  4. 打開eclipse浪感,在Help->Install New Software后的彈出窗點(diǎn)擊Available Software Sites昔头,刪除設(shè)置過的Activiti資源信息饼问,add確認(rèn)安裝。


在IDEA中使用Activiti

  1. 點(diǎn)擊菜單【File】-->【Settings...】打開【Settings】窗口揭斧。

點(diǎn)擊左側(cè)【Plugins】按鈕莱革,在右側(cè)輸出"actiBPM",點(diǎn)擊下面的【Search in repositories】鏈接會(huì)打開【Browse Repositories】窗口

  1. 進(jìn)入【Browse Repositories】窗口讹开,選中左側(cè)的【actiBPM】盅视,點(diǎn)擊右側(cè)的【Install】按鈕,開始安裝旦万。

  2. 安裝完成后闹击,會(huì)提示【Restart IntelliJ IDEA】,重啟IDEA即可完成安裝成艘。


如何更優(yōu)雅地使用Activiti

隨著微服務(wù)的普及赏半,Rest風(fēng)格的編碼規(guī)范越來被推崇贺归,因而我們使用Activiti Rest模塊進(jìn)行Activiti統(tǒng)一Rest調(diào)用。

1. Activiti REST模塊介紹

Activiti-rest是Activiti提供的一組可以直接操作工作流引擎的REST API断箫,使用者可以在自己應(yīng)用中直接調(diào)用Activiti-rest接口拂酣。

1.1 使用REST的好處

  1. 簡(jiǎn)單化:利用現(xiàn)有模塊(activiti-rest.war)代替直接API調(diào)用

  2. 標(biāo)準(zhǔn)化:各個(gè)系統(tǒng)根據(jù)rest模塊的接口規(guī)范訪問REST資源,統(tǒng)一處理仲义;對(duì)于工作流平臺(tái)來說此特性尤為突出

  3. 擴(kuò)展性:如果官方提供的REST接口還不能滿足可以繼續(xù)在其基礎(chǔ)上進(jìn)行擴(kuò)展以滿足業(yè)務(wù)系統(tǒng)(平臺(tái))的需求

1.2 不適合使用REST的場(chǎng)景

業(yè)務(wù)數(shù)據(jù)與流程數(shù)據(jù)分離:就像kft-activiti-demo中普通表單的演示一樣婶熬,業(yè)務(wù)數(shù)據(jù)保存在一張單獨(dú)設(shè)計(jì)的表中,而不是把表單數(shù)據(jù)保存在引擎的變量表中埃撵,所以對(duì)于這樣的場(chǎng)景中需要聯(lián)合事務(wù)管理的就不能使用REST了赵颅,例如:?jiǎn)?dòng)流程、任務(wù)完成盯另、業(yè)務(wù)與流程數(shù)據(jù)聯(lián)合查詢性含。

1.3 部署Rest模塊

5.11版本開始不再使用ant腳本的方式啟動(dòng)demo,并且把a(bǔ)ctiviti-explorer和activiti-rest分離并分別提供一個(gè)war包鸳惯,在wars目錄可以找到它商蕴。

把a(bǔ)ctiviti-rest.war解壓到Web服務(wù)器的應(yīng)用部署目錄(例如tomcat的webapps),根據(jù)實(shí)際需求修改activiti-rest/WEB-INF/classes/db.properties里面的數(shù)據(jù)庫配置后啟動(dòng)應(yīng)用芝发。

可以通過REST工具測(cè)試是否部署成功可以正常的提供服務(wù)绪商,例如Chrome的插件REST Console,或者通過Spring MVC提供的RestTemplate辅鲸。

2. 訪問REST資源

對(duì)于REST模塊提供的接口可以參考用戶手冊(cè)的REST API章節(jié)格郁,有著詳細(xì)的介紹(包括URL和參數(shù)含義)。

2.1 身份認(rèn)證

REST接口的大部分功能都需要驗(yàn)證独悴,默認(rèn)使用Basic Access Authentication(基本連接認(rèn)證)例书,所以在訪問資源時(shí)要在header中添加驗(yàn)證信息,當(dāng)然為了安全期間把用戶名和密碼進(jìn)行base 64位加密刻炒。

可以在用戶登陸之后把用戶名和密碼進(jìn)行加密并設(shè)置到session中决采,這樣在前端就可以直接通過Ajax方式獲取資源了:

import jodd.util.Base64;
String base64Code = "Basic " + Base64.encodeToString(user.getId() + ":" + user.getPassword());
session.setAttribute("BASE_64_CODE", base64Code);

2.2 通過Ajax方式讀取資源

下面通過kft-activiti-demo中的代碼片段介紹:

$.ajax({
    type: "get",
    url: REST_URL + 'process-definition/' + processDefinitionId + '/form',
    beforeSend: function(xhr) {
        xhr.setRequestHeader('Authorization', BASE_64_CODE);
    },
    dataType: 'html',
    success: function(form) {
        // 獲取的form是字符行,html格式直接顯示在對(duì)話框內(nèi)就可以了坟奥,然后用form包裹起來
        $(dialog).html(form).wrap("

在第5行處設(shè)置了ajax請(qǐng)求的header信息树瞭,這樣REST模塊就可以通過header的信息進(jìn)行身份認(rèn)證,通過之后就可以執(zhí)行資源請(qǐng)求并返回處理結(jié)果爱谁。

2.3 通過Java方式讀取資源

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestLogin {

  public static void main(String[] args) throws Exception, IOException {
    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpPost postRequest = new HttpPost("http://localhost:8080/activiti-rest/service/login");

    StringEntity input = new StringEntity("{\"userId\":\"kermit\",\"password\":\"kermit\"}");
    input.setContentType("application/json");
    postRequest.setEntity(input);

    HttpResponse response = httpClient.execute(postRequest);

    BufferedReader br = new BufferedReader(new InputStreamReader((response.getEntity().getContent())));

    String output;
    System.out.println("Output from Server .... \n");
    while ((output = br.readLine()) != null) {
      System.out.println(output);
    }

    httpClient.getConnectionManager().shutdown();
  }
}

3. Ajax跨域問題解決辦法

  1. 把REST模塊和應(yīng)用部署在同一個(gè)Web服務(wù)器中(廢話……)

  2. 等待官方提供JSONP的支持晒喷,JIRA Issue:ACT-1534

  3. 利用后臺(tái)代理方式,把請(qǐng)求的URL發(fā)送給后臺(tái)代理服務(wù)器访敌,獲取數(shù)據(jù)之后再把結(jié)果返回給前臺(tái)


在WEB界面繪制流程圖

Activiti Modeler 組件凉敲,是一套支持WEB界面實(shí)現(xiàn)流程繪制的服務(wù)組件,允許使用方自定義流程。

1.下載 Activiti-activiti-5.22.0 包

下載地址:https://github.com/Activiti/Activiti/tree/activiti-5.22.0/

  1. 1 提取 Activiti-activiti-5.22.0 包內(nèi)容爷抓,放置到項(xiàng)目中

1.1.1 從 Activiti-activiti-5.22.0>modules>activiti-webapp-explorer2>src>main>webapp 下提取diagram-viewer雨效,editor-app,modeler.html 放置到 resource/static 中1.1.2 從Activiti-activiti-5.22.0>modules>activiti-webapp-explorer2>src>main>resources 下提取stencilset.json 放置到 resource/static 中1.1.3 從Activiti-activiti-5.22.0>modules>activiti-modeler>src>main>java>org>activiti>rest>editor 下提取 ModelEditorJsonRestResource.java废赞,ModelSaveRestResource.java 徽龟,StencilsetRestResource.java放置到項(xiàng)目中1.1.4 修改ModelEditorJsonRestResource.java

@RestController
@RequestMapping(value = "/service") //添加

1.1.5 修改ModelSaveRestResource.java

@RestController
@RequestMapping("/service")
public class ModelSaveRestResource implements ModelDataJsonConstants {

  protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);
  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private ObjectMapper objectMapper;

  /**
   * 保存模型
   * @param modelId
   * @param json_xml
   * @param svg_xml
   * @param name
   * @param description
   */
  @RequestMapping(value = "/model/{modelId}/save", method = RequestMethod.PUT)
  public void saveModel(@PathVariable String modelId, @RequestParam String json_xml,
                        @RequestParam  String svg_xml,@RequestParam String name, @RequestParam String description) {
      try {

          // 獲取模型信息并更新模型信息
          ObjectMapper objectMapper = new ObjectMapper();
          Model model = repositoryService.getModel(modelId);
          ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
          modelJson.put(MODEL_NAME, name);
          modelJson.put(MODEL_DESCRIPTION, description);
          model.setMetaInfo(modelJson.toString());
          model.setName(name);
          repositoryService.saveModel(model);
          repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));

          // 基于模型信息做流程部署
          InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
          TranscoderInput input = new TranscoderInput(svgStream);
          PNGTranscoder transcoder = new PNGTranscoder();
          ByteArrayOutputStream outStream = new ByteArrayOutputStream();
          TranscoderOutput output = new TranscoderOutput(outStream);
          transcoder.transcode(input, output);
          final byte[] result = outStream.toByteArray();
          repositoryService.addModelEditorSourceExtra(model.getId(), result);
          outStream.close();
          Model modelData = repositoryService.getModel(modelId);
          ObjectNode modelNode = (ObjectNode) objectMapper.readTree(repositoryService.getModelEditorSource(modelData.getId()));
          byte[] bpmnBytes = null;
          BpmnModel model2 = new BpmnJsonConverter().convertToBpmnModel(modelNode);
          bpmnBytes = new BpmnXMLConverter().convertToXML(model2);
          String processName = modelData.getName() + ".bpmn";
          Deployment deployment = repositoryService.createDeployment()
                  .name(modelData.getName())
                  .addString(processName, StringUtils.toEncodedString(bpmnBytes, Charset.forName("UTF-8")))
                  .deploy();
      } catch (Exception e) {
          LOGGER.error("模型保存失敗", e);
          throw new ActivitiException("模型保存失敗", e);
      }

  }
}

1.1.6 修改StencilsetRestResource.java

@RestController
@RequestMapping(value = "/service")
public class StencilsetRestResource {

  /**
   * 加載模板集配置
   * @return
   */
  @RequestMapping(value="/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
  public @ResponseBody String getStencilset() {
      try {
          InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("static/stencilset-cn.json");
          return IOUtils.toString(stencilsetStream, "utf-8");
      } catch (Exception e) {
          throw new ActivitiException("加載模板集配置失敗", e);
      }
  }
}

1.1.7 修改app-cfg.js

ACTIVITI.CONFIG = {
// 'contextRoot' : '/activiti-explorer/service',
 'contextRoot' : '/activiti/service',
}
  1. 2 activiti 繪制器的漢化

使用漢化的stencilset.json替換resource/static 下的stencilset.json

點(diǎn)擊下載

  1. 3 實(shí)現(xiàn)控制類,用于模型的構(gòu)建

創(chuàng)建ActivitiModelController.java

@RestController
public class ActivitiModelController {

    public static Logger logger = Logger.getLogger(ActivitiModelController.class);
    @Autowired
    private ProcessEngine processEngine;                    // 定義流程引擎
    @Autowired
    private ObjectMapper objectMapper;
    private final static Integer MODEL_REVISION = 1;       // 模型的版本
    private final static String ACTIVITI_REDIRECT_MODELER_INDEX = "/activitiService/modeler.html?modelId=";         // modeler.html頁面地址
    private final static String ACTIVITI_NAMESPACE_VALUE = "http://b3mn.org/stencilset/bpmn2.0#";                    // 默認(rèn)的空間值
    private final static String ACTIVITI_ID_VALUE = "canvas";                                                             // 默認(rèn)ID值

    /**
     * 新建一個(gè)模型
     * @param response
     * @throws IOException
     */
    @RequestMapping("/activiti-ui.html")
    public void newModel(HttpServletResponse response) throws IOException {
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 初始化空的流程資源模型唉地,填充信息并持久化模型
        Model model = repositoryService.newModel();
        String uuid = UUUID.randomUUID().toString().replaceAll("-", "");
        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, uuid);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "");
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, MODEL_REVISION);
        model.setName(uuid);
        model.setKey(uuid);
        model.setMetaInfo(modelNode.toString());
        repositoryService.saveModel(model);

        // 將 editorNode  數(shù)據(jù)填充到模型中, 并做頁面的重定向
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", ACTIVITI_ID_VALUE);
        editorNode.put("resourceId", ACTIVITI_ID_VALUE);
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",ACTIVITI_NAMESPACE_VALUE);
        editorNode.put("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(model.getId(),editorNode.toString().getBytes("utf-8"));
        response.sendRedirect(ACTIVITI_REDIRECT_MODELER_INDEX + model.getId());
    }
}
  1. 4 驗(yàn)證 activiti modeler

http://ip:port/activiti/activiti-ui.html

activiti modeler

搭建工作流平臺(tái)

參考開源社區(qū):

  1. Spring平臺(tái)整合activiti工作流引擎實(shí)例

  2. springboot+activiti+angular 這是spring boot框架集成activiti工作流實(shí)現(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末据悔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耘沼,更是在濱河造成了極大的恐慌极颓,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件群嗤,死亡現(xiàn)場(chǎng)離奇詭異菠隆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狂秘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門骇径,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人者春,你說我怎么就攤上這事破衔。” “怎么了钱烟?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵晰筛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我拴袭,道長(zhǎng)读第,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任拥刻,我火速辦了婚禮怜瞒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泰佳。我一直安慰自己盼砍,他們只是感情好尘吗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布逝她。 她就那樣靜靜地躺著,像睡著了一般睬捶。 火紅的嫁衣襯著肌膚如雪黔宛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天擒贸,我揣著相機(jī)與錄音臀晃,去河邊找鬼觉渴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛徽惋,可吹牛的內(nèi)容都是我干的案淋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼险绘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼踢京!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宦棺,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤瓣距,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后代咸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹈丸,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年呐芥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逻杖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡思瘟,死狀恐怖弧腥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潮太,我是刑警寧澤管搪,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站铡买,受9級(jí)特大地震影響更鲁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奇钞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一澡为、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧景埃,春花似錦媒至、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至完慧,卻和暖如春谋旦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工册着, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拴孤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓甲捏,卻偏偏與公主長(zhǎng)得像演熟,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子司顿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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