Java業(yè)務(wù)校驗(yàn)工具實(shí)現(xiàn)

一级遭、背景

在我們?nèi)粘=涌陂_(kāi)發(fā)過(guò)程中芬位,可能要面對(duì)一些稍微復(fù)雜一些的業(yè)務(wù)邏輯代碼的編寫(xiě),在執(zhí)行真正的業(yè)務(wù)邏輯前笼呆,往往要進(jìn)行一系列的前期校驗(yàn)工作熊响,校驗(yàn)可以分為參數(shù)合法性校驗(yàn)業(yè)務(wù)數(shù)據(jù)校驗(yàn)

參數(shù)合法性校驗(yàn)比如最常見(jiàn)的校驗(yàn)參數(shù)值非空校驗(yàn)诗赌、格式校驗(yàn)汗茄、最大值最小值校驗(yàn)等,可以通過(guò)Hibernate Validator框架實(shí)現(xiàn)铭若,本文不具體講解剔难。業(yè)務(wù)數(shù)據(jù)校驗(yàn)通常與實(shí)際業(yè)務(wù)相關(guān),比如提交訂單接口奥喻,我們可能需要校驗(yàn)商品是否合法偶宫、庫(kù)存是否足夠、客戶余額是否足夠环鲤、還有其他的一些風(fēng)控校驗(yàn)纯趋。我們的代碼可能看起來(lái)像是這樣的:

public ApiResult<OrderSubmitVo> submitOrder(OrderSubmitDto orderSubmitDto) {
    // 業(yè)務(wù)校驗(yàn)1

    // 業(yè)務(wù)校驗(yàn)2

    // 業(yè)務(wù)校驗(yàn)3

    // 業(yè)務(wù)校驗(yàn)n...

    // 執(zhí)行真正的業(yè)務(wù)邏輯

    return ApiResult.success();
}

二、問(wèn)題

  • 實(shí)現(xiàn)不夠優(yōu)雅

上述代碼在版本迭代的過(guò)程中冷离,還可能陸陸續(xù)續(xù)增加/修改一些校驗(yàn)邏輯吵冒,如果業(yè)務(wù)邏輯校驗(yàn)的代碼都耦合在核心業(yè)務(wù)邏輯中,這樣實(shí)現(xiàn)其實(shí)是不夠優(yōu)雅西剥,不符合設(shè)計(jì)原則的單一職責(zé)原則和開(kāi)閉原則痹栖。

  • 校驗(yàn)代碼無(wú)法復(fù)用

如果某個(gè)業(yè)務(wù)校驗(yàn)代碼需要在其他業(yè)務(wù)中也會(huì)用到,那我們則需要將相同的代碼復(fù)制一份至業(yè)務(wù)代碼中瞭空,比如校驗(yàn)用戶狀態(tài)揪阿,在很多業(yè)務(wù)校驗(yàn)中都需要校驗(yàn),如果校驗(yàn)邏輯有些許更改的話咆畏,那么所有涉及到的地方都要同步修改南捂,這樣不利于系統(tǒng)維護(hù)。

  • 校驗(yàn)邏輯無(wú)法按照順序依賴執(zhí)行旧找,并且校驗(yàn)過(guò)程中產(chǎn)生的數(shù)據(jù)后續(xù)獲取不便

如果我們將上述代碼中的各個(gè)校驗(yàn)邏輯封裝成獨(dú)立的子方法溺健,那有可能存在業(yè)務(wù)校驗(yàn)2要依賴于業(yè)務(wù)校驗(yàn)1的數(shù)據(jù)結(jié)果,并且在業(yè)務(wù)校驗(yàn)過(guò)程中產(chǎn)生的數(shù)據(jù)在后續(xù)執(zhí)行真正的業(yè)務(wù)邏輯的時(shí)候是需要用得到的钮蛛。

三鞭缭、校驗(yàn)工具實(shí)現(xiàn)思路

我們要寫(xiě)的校驗(yàn)工具至少要解決上面所說(shuō)的三個(gè)問(wèn)題

  • 業(yè)務(wù)校驗(yàn)代碼與核心業(yè)務(wù)邏輯代碼解耦

  • 同一個(gè)校驗(yàn)器可以用于多個(gè)業(yè)務(wù)剖膳,提高代碼的復(fù)用性和可維護(hù)性

  • 校驗(yàn)代碼可以按照指定順序執(zhí)行,并且校驗(yàn)過(guò)程中產(chǎn)生的數(shù)據(jù)可以后續(xù)傳遞

在用zuul來(lái)做網(wǎng)關(guān)服務(wù)的時(shí)候岭辣,我獲得了一些靈感潮秘,
zuul中的filterType用來(lái)區(qū)分請(qǐng)求路由到目標(biāo)之前、處理目標(biāo)請(qǐng)求易结、目標(biāo)請(qǐng)求返回后的類型枕荞,filterOrder用來(lái)指定過(guò)濾器的執(zhí)行順序,RequestContext為請(qǐng)求上下文搞动,RequestContext繼承自ConcurrentHashMap躏精,且與ThreadLocal綁定保證線程安全,請(qǐng)求上下文中的數(shù)據(jù)在一次請(qǐng)求的所有過(guò)濾器中可以獲取鹦肿,很好的完成了數(shù)據(jù)傳遞矗烛。

首先我們需要定義一個(gè)校驗(yàn)器注解,注解中指定業(yè)務(wù)類型和執(zhí)行順序箩溃,在校驗(yàn)器上加上該注解表明這是一個(gè)校驗(yàn)器瞭吃。定義一個(gè)校驗(yàn)器上下文,在業(yè)務(wù)校驗(yàn)執(zhí)行過(guò)程中產(chǎn)生的數(shù)據(jù)可以通過(guò)上下文進(jìn)行傳遞涣旨。定義一個(gè)校驗(yàn)器基類歪架,校驗(yàn)器繼承基類,并實(shí)現(xiàn)其中的具體校驗(yàn)方法霹陡。定義一個(gè)校驗(yàn)器的統(tǒng)一執(zhí)行器和蚪,執(zhí)行器可以根據(jù)業(yè)務(wù)類型找出所有帶有校驗(yàn)器注解并且是指定業(yè)務(wù)類型的校驗(yàn)器列表,根據(jù)校驗(yàn)器注解中的執(zhí)行順序排序后烹棉,遍歷所有校驗(yàn)器列表調(diào)用校驗(yàn)方法攒霹。如果校驗(yàn)過(guò)程中校驗(yàn)失敗,則拋出校驗(yàn)異常中斷業(yè)務(wù)執(zhí)行浆洗。

以上為大概的實(shí)現(xiàn)思路催束,具體的實(shí)現(xiàn)代碼如下:

四、show me your code

  • Validator.java
import java.lang.annotation.*;

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/23 13:58
 * @description: 業(yè)務(wù)校驗(yàn)注解
 */
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Validator {
    /**
     * 業(yè)務(wù)類型伏社,同一個(gè)校驗(yàn)器可以指定多個(gè)業(yè)務(wù)類型
     *
     * @return
     */
    String[] validateTypes();

    /**
     * 執(zhí)行順序,數(shù)值越小越先執(zhí)行
     *
     * @return
     */
    int validateOrder();
}

Validator校驗(yàn)注解抠刺,在校驗(yàn)器的類上加上該注解則表明為業(yè)務(wù)校驗(yàn)器,validateTypes表示業(yè)務(wù)類型洛口,同一個(gè)校驗(yàn)器可以指定多個(gè)業(yè)務(wù)類型矫付,多個(gè)業(yè)務(wù)類型可以復(fù)用同一個(gè)校驗(yàn)器凯沪,validateOrder表示執(zhí)行順序第焰,數(shù)值越小越先被執(zhí)行。

  • ValidatorContext.java

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/9/11 14:56
 * @description: 校驗(yàn)器上下文妨马,與當(dāng)前線程綁定
 */
public class ValidatorContext extends ConcurrentHashMap<String, Object> {
    /**
     * 請(qǐng)求對(duì)象
     */
    public Object requestDto;

    protected static final ThreadLocal<? extends ValidatorContext> threadLocal = ThreadLocal.withInitial(() -> new ValidatorContext());

    /**
     * 獲取當(dāng)前線程的上下文
     *
     * @return
     */
    public static ValidatorContext getCurrentContext() {
        ValidatorContext context = threadLocal.get();
        return context;
    }


    /**
     * 設(shè)值
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        if (value != null) put(key, value);
        else remove(key);
    }


    /**
     * 獲取String值
     *
     * @param key
     * @return
     */
    public String getString(String key) {
        return (String) get(key);
    }

    /**
     * 獲取Integer值
     *
     * @param key
     * @return
     */
    public Integer getInteger(String key) {
        return (Integer) get(key);
    }

    /**
     * 獲取Boolean值
     *
     * @param key
     * @return
     */
    public Boolean getBoolean(String key) {
        return (Boolean) get(key);
    }

    /**
     * 獲取對(duì)象
     *
     * @param key
     * @param <T>
     * @return
     */
    public <T> T getClazz(String key) {
        return (T) get(key);
    }

    /**
     * 獲取Long值
     *
     * @param key
     * @return
     */
    public Long getLong(String key) {
        return (Long) get(key);
    }


    public <T> T getRequestDto() {
        return (T) requestDto;
    }

    public void setRequestDto(Object requestDto) {
        this.requestDto = requestDto;
    }

ValidatorContext為請(qǐng)求上下文挺举,與當(dāng)前請(qǐng)求線程綁定杀赢,繼承自ConcurrentHashMap,requestDto屬性為接口請(qǐng)求入?yún)?duì)象湘纵,提供get/set方法使得在上下文中能更加便捷的獲取請(qǐng)求入?yún)?shù)據(jù)脂崔。

  • ValidatorTemplate.java

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/23 11:51
 * @description: 校驗(yàn)器模板,業(yè)務(wù)校驗(yàn)器需繼承模板類
 */
@Slf4j
@Component
public abstract class ValidatorTemplate {

    /**
     * 校驗(yàn)方法
     */
    public void validate() {
        try {
            validateInner();
        } catch (ValidateException e) {
            log.error("業(yè)務(wù)校驗(yàn)失敗", e);
            throw e;
        } catch (Exception e) {
            log.error("業(yè)務(wù)校驗(yàn)異常", e);
            ValidateException validateException = new ValidateException(ResultEnum.VALIDATE_ERROR);
            throw validateException;
        }
    }

    /**
     * 校驗(yàn)方法,由子類具體實(shí)現(xiàn)
     *
     * @throws ValidateException
     */
    protected abstract void validateInner() throws ValidateException;
}

校驗(yàn)器抽象類梧喷,具體的校驗(yàn)器需要繼承該類砌左,并且實(shí)現(xiàn)具體的validateInner校驗(yàn)方法。

  • ValidatorTemplateProxy.java


/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/25 18:03
 * @description: ValidatorTemplate代理類
 */
@Data
@AllArgsConstructor
public class ValidatorTemplateProxy extends ValidatorTemplate implements Comparable<ValidatorTemplateProxy> {
    private ValidatorTemplate validatorTemplate;
    private String validateType;
    private int validateOrder;

    @Override
    public int compareTo(ValidatorTemplateProxy o) {
        return Integer.compare(this.getValidateOrder(), o.getValidateOrder());
    }

    @Override
    protected void validateInner() throws ValidateException {
        validatorTemplate.validateInner();
    }
}

ValidatorTemplate類的代理類铺敌,實(shí)現(xiàn)了Comparable排序接口汇歹,便于校驗(yàn)器按照validateOrder屬性排序,并且將校驗(yàn)器中的注解轉(zhuǎn)化為代理類中的兩個(gè)屬性字段偿凭,方便執(zhí)行過(guò)程中的統(tǒng)一日志打印产弹。

  • ValidateProcessor.java
import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/25 18:02
 * @description: 執(zhí)行器
 */
@Slf4j
@Component
public class ValidateProcessor {

    /**
     * 執(zhí)行業(yè)務(wù)類型對(duì)應(yīng)的校驗(yàn)器
     *
     * @param validateType
     */
    public void validate(String validateType) {
        if (StringUtils.isEmpty(validateType)) {
            throw new IllegalArgumentException("validateType cannot be null");
        }
        long start = System.currentTimeMillis();
        log.info("start validate,validateType={}弯囊,ValidatorContext={}", validateType, ValidatorContext.getCurrentContext().toString());
        List<ValidatorTemplateProxy> validatorList = getValidatorList(validateType);
        if (CollectionUtils.isEmpty(validatorList)) {
            log.info("validatorList is empty");
            return;
        }
        ValidatorTemplateProxy validateProcessorProxy;
        for (ValidatorTemplateProxy validatorTemplate : validatorList) {
            validateProcessorProxy = validatorTemplate;
            log.info("{} is running", validateProcessorProxy.getValidatorTemplate().getClass().getSimpleName());
            validatorTemplate.validate();
        }
        log.info("end validate痰哨,validateType={},ValidatorContext={}匾嘱,time consuming {} ms", validateType,
                ValidatorContext.getCurrentContext().toString(), (System.currentTimeMillis() - start));
    }


    /**
     * 根據(jù)Validator注解的validateType獲取所有帶有該注解的校驗(yàn)器
     *
     * @param validateType
     * @return
     */
    private List<ValidatorTemplateProxy> getValidatorList(String validateType) {
        List<ValidatorTemplateProxy> validatorTemplateList = new LinkedList<>();
        Map<String, Object> map = SpringUtil.getApplicationContext().getBeansWithAnnotation(Validator.class);
        String[] validateTypes;
        int validateOrder;
        Annotation annotation;
        for (Map.Entry<String, Object> item : map.entrySet()) {
            annotation = item.getValue().getClass().getAnnotation(Validator.class);
            validateTypes = ((Validator) annotation).validateTypes();
            validateOrder = ((Validator) annotation).validateOrder();
            if (item.getValue() instanceof ValidatorTemplate) {
                if (Arrays.asList(validateTypes).contains(validateType)) {
                    validatorTemplateList.add(new ValidatorTemplateProxy((ValidatorTemplate) item.getValue(), validateType, validateOrder));
                }
            } else {
                log.info("{}not extend from ValidatorTemplate", item.getKey());
            }
        }
        Collections.sort(validatorTemplateList);
        return validatorTemplateList;
    }
}

業(yè)務(wù)校驗(yàn)的執(zhí)行器斤斧,getValidatorList方法根據(jù)validateType值獲取所有帶有該validateType值的校驗(yàn)器,并將其封裝成ValidatorTemplateProxy代理類霎烙,然后再做排序折欠。validate為統(tǒng)一的業(yè)務(wù)校驗(yàn)方法。

  • ValidateException.java


/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/4/4 6:34 PM
 * @description: 校驗(yàn)異常
 */
public class ValidateException extends RuntimeException {
    // 異常碼
    private Integer code;

    public ValidateException() {
    }

    public ValidateException(String message) {
        super(message);
    }

    public ValidateException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

}

ValidateException為校驗(yàn)失敗時(shí)吼过,拋出的業(yè)務(wù)校驗(yàn)異常類锐秦。

  • ValidateTypeConstant.java

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/30 15:16
 * @description:
 */
public class ValidateTypeConstant {
    /**
     * 提交訂單校驗(yàn)
     */
    public static final String ORDER_SUBMIT = "order_submit";
}

ValidateTypeConstant為定義validateType業(yè)務(wù)校驗(yàn)類型的常量類。

五盗忱、使用樣例

以訂單提交為例酱床,我們首先定義了兩個(gè)個(gè)基本的校驗(yàn)器,下單商品信息校驗(yàn)器趟佃、客戶狀態(tài)校驗(yàn)器扇谣,均為偽代碼實(shí)現(xiàn)。

  • OrderSubmitProductValidator.java

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/30 15:34
 * @description: 商品狀態(tài)以及庫(kù)存校驗(yàn)
 */
@Component
@Slf4j
@Validator(validateTypes = ValidateTypeConstant.ORDER_SUBMIT, validateOrder = 1)
public class OrderSubmitProductValidator extends ValidatorTemplate {
    @Override
    protected void validateInner() throws ValidateException {
        ValidatorContext validatorContext = ValidatorContext.getCurrentContext();
        OrderSubmitDto orderSubmitDto = validatorContext.getRequestDto();
        // 獲取商品信息并校驗(yàn)商品狀態(tài)
        List<ProductShelfVo> productShelfVoList = new ArrayList<>();
        if (0 == 1) {
            throw new ValidateException("商品已下架");
        }
        // 將商品信息設(shè)置至上下文中
        validatorContext.set("productShelfVoList", productShelfVoList);
    }
}
  • OrderSubmitCustomerValidator.java

/**
 * @author: 會(huì)跳舞的機(jī)器人
 * @date: 2019/10/30 19:24
 * @description:
 */
@Component
@Slf4j
@Validator(validateTypes = ValidateTypeConstant.ORDER_SUBMIT, validateOrder = 2)
public class OrderSubmitCustomerValidator extends ValidatorTemplate {
    @Override
    protected void validateInner() throws ValidateException {
        ValidatorContext validatorContext = ValidatorContext.getCurrentContext();
        String customerNo = validatorContext.getString("customerNo");
        if (StringUtils.isEmpty(customerNo)) {
            throw new IllegalArgumentException("客戶編號(hào)為空");
        }
        // 獲取客戶信息并校驗(yàn)客戶狀態(tài)
        CustomerVo customer = new CustomerVo();
        if (0 == 1) {
            throw new ValidateException("客戶限制交易");
        }
    }
}

在提交訂單的業(yè)務(wù)邏輯的代碼中使用:

/**
 * 提交訂單
 *
 * @param orderSubmitDto
 * @return
 */
public ApiResult<OrderSubmitVo> submitOrder(OrderSubmitDto orderSubmitDto) {
    // 業(yè)務(wù)校驗(yàn)
    ValidatorContext validatorContext = ValidatorContext.getCurrentContext();
    validatorContext.setRequestDto(orderSubmitDto);
    validateProcessor.validate(ValidateTypeConstant.ORDER_SUBMIT);
    // 從上下文中獲取下單商品信息
    List<ProductShelfVo> productShelfVoList = validatorContext.getClazz("productShelfVoList");

    // 后續(xù)業(yè)務(wù)邏輯處理
    return ApiResult.success();
}

通過(guò)使用上述封裝的校驗(yàn)工具后闲昭,業(yè)務(wù)代碼與校驗(yàn)代碼解耦罐寨,后續(xù)要增加/修改業(yè)務(wù)校驗(yàn)邏輯時(shí)候,我們只需要增加/修改相應(yīng)的校驗(yàn)器即可序矩,不必改動(dòng)到主業(yè)務(wù)邏輯鸯绿。為了我們能更簡(jiǎn)單和方便找到某個(gè)業(yè)務(wù)邏輯對(duì)應(yīng)所有的校驗(yàn)器,我們?cè)诿r?yàn)器的時(shí)候可以加上業(yè)務(wù)類型的前綴。

六瓶蝴、總結(jié)

1毒返、在開(kāi)發(fā)過(guò)程中,我們遇到一些“煩人”問(wèn)題的時(shí)候舷手,要想辦法解決它拧簸,而不是忽略不管它,通過(guò)解決問(wèn)題可以提高我們的技術(shù)能力男窟。

2盆赤、要善于從其他優(yōu)秀的技術(shù)框架學(xué)習(xí)其實(shí)現(xiàn)思路。

3歉眷、以上校驗(yàn)工具只是一個(gè)簡(jiǎn)單實(shí)現(xiàn)弟劲,解決的問(wèn)題只是筆者在開(kāi)發(fā)過(guò)程中遇到的問(wèn)題,可能并不一定具有通用性姥芥。


如果文章對(duì)你有幫助的話兔乞,給文章點(diǎn)個(gè)贊吧。

如果有寫(xiě)得不正確的地方凉唐,歡迎指出庸追。

文章首發(fā)公眾號(hào):會(huì)跳舞的機(jī)器人,歡迎掃碼關(guān)注台囱。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淡溯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子簿训,更是在濱河造成了極大的恐慌咱娶,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强品,死亡現(xiàn)場(chǎng)離奇詭異膘侮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)的榛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)琼了,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人夫晌,你說(shuō)我怎么就攤上這事雕薪。” “怎么了晓淀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵所袁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我凶掰,道長(zhǎng)燥爷,這世上最難降的妖魔是什么蜈亩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮局劲,結(jié)果婚禮上勺拣,老公的妹妹穿的比我還像新娘奶赠。我一直安慰自己鱼填,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布毅戈。 她就那樣靜靜地躺著苹丸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苇经。 梳的紋絲不亂的頭發(fā)上赘理,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音扇单,去河邊找鬼商模。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蜘澜,可吹牛的內(nèi)容都是我干的施流。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鄙信,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞪醋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起装诡,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤银受,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鸦采,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體宾巍,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年渔伯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜀漆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咱旱,死狀恐怖确丢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吐限,我是刑警寧澤鲜侥,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诸典,受9級(jí)特大地震影響描函,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一舀寓、第九天 我趴在偏房一處隱蔽的房頂上張望胆数。 院中可真熱鬧,春花似錦互墓、人聲如沸必尼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)判莉。三九已至,卻和暖如春育谬,著一層夾襖步出監(jiān)牢的瞬間券盅,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工膛檀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锰镀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓咖刃,卻偏偏與公主長(zhǎng)得像泳炉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子僵缺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353