寫出優(yōu)雅的業(yè)務(wù)代碼(1):項目中的模版方法竿裂,策略模式

關(guān)鍵字:

  1. 如何寫好業(yè)務(wù)代碼
  2. 業(yè)務(wù)架構(gòu)
  3. 設(shè)計模式
  4. 模版方法
  5. 策略模式
  6. 工廠模式

本文概要:

對于做web開發(fā)的java程序員來說许帐,如何寫出更好看的業(yè)務(wù)代碼。本文會展示利用設(shè)計模式中模版方法主经,策略荣暮,工廠3種模式來優(yōu)化平鋪直敘的代碼。

業(yè)務(wù)簡介:

開始之前需要先了解一下業(yè)務(wù)罩驻。

  1. 業(yè)務(wù)是通過調(diào)用支付寶接口來做支付訂單穗酥。
  2. 業(yè)務(wù)中有10種訂單類型。
  3. 通過接口參數(shù)里的payType參數(shù)確定是哪種訂單惠遏,然后執(zhí)行對應(yīng)的訂單分支邏輯迷扇,調(diào)用阿里支付,返回交易編號

優(yōu)化前的部分代碼如下:

  1. http接口接收訂單類型參數(shù)爽哎,然后調(diào)用service的getPayInfo方法時傳入這個類型蜓席。
    001
  2. getPayInfo方法根據(jù)參數(shù)payType做switch case,這本身沒有太大問題课锌,但是清注意厨内,每一個case里,代碼都分為2個步驟渺贤,步驟1準(zhǔn)備參數(shù)雏胃,步驟2調(diào)用與case對應(yīng)的方法,并且每個方法的參數(shù)個數(shù)一樣志鞍,類型除了最后一個之外瞭亮,也都是一樣的。
    002
  3. 上面case語句里調(diào)用的方法如下圖固棚,可以分為2個或3個步驟统翩,步驟1組裝參數(shù)對象,步驟2調(diào)用alipayService.getTradeNo方法此洲,步驟3根據(jù)步驟2的返回內(nèi)容做后續(xù)處理厂汗,最后返回阿里支付的交易編號。
    003

優(yōu)化前代碼的一些問題

  1. 對于上面圖002中的getPayInfo方法呜师,如果業(yè)務(wù)要在增加一種訂單類型娶桦,那么就需要再增加一個case的同時,增加case中的步驟1汁汗,步驟2代碼衷畦。這樣的話就會導(dǎo)致每次增加一種新訂單類型都需要維護很大的一大坨代碼。而且如果某個現(xiàn)有訂單類型的步驟1知牌,步驟2邏輯需要改動祈争,也需要在這個switch case里修改。如果用高大上的語言來描述送爸,那就是沒有符合開閉原則铛嘱,沒有做到單一職責(zé)原則,沒有做到高內(nèi)聚低耦合等等袭厂。

怎么來優(yōu)化呢

  1. 上面的代碼圖002中service里的getPayInfo方法中墨吓, case里寫了大量(省略了7個case分支)的重復(fù)代碼,各個case中的步驟1纹磺,步驟2都是可以抽象出來的帖烘。
  2. 圖002中case里調(diào)用各自的方法獲取tradeNo。這些方法在圖003中可以看出來也有共通性橄杨,也可以抽象出來秘症。
  3. 從整體的代碼邏輯上來看,圖002中的每個case分支式矫,及其后續(xù)操作乡摹,似乎都有著同樣的共性,只有少部分的邏輯有各自的特性采转,那么很容易想到用模版方法模式來做優(yōu)化聪廉。因為有很多case分支,也很容易聯(lián)想到策略模式了故慈。
  4. 優(yōu)化的思路如下:
    1. 把不同的訂單類型都當(dāng)作不同的策略板熊,那么也就是每一個case中的邏輯都是一個不同的策略;
    2. 因為每一個訂單類型的代碼流程大致都相同察绷,只有少數(shù)不同的步驟干签,那么就定義一個模版類,將相同的步驟統(tǒng)一寫在模版類里拆撼,不同的步驟容劳,讓策略類去繼承模版類后,自己去實現(xiàn)闸度;
    3. 把switch case轉(zhuǎn)移到一個工廠類里鸭蛙,通過工廠類生成不同的策略對象;
    4. 外層代碼調(diào)用工廠類返回對象的模版方法筋岛,即可完成訂單支付流程娶视。

優(yōu)化之后的代碼

  1. 首先創(chuàng)建一個模版類的接口IOrderAlipayStrategy,模版類實現(xiàn)這個接口睁宰,該接口只有一個方法payThroughAlipay肪获,具體使用請看 2.
/**
 * @Author: yesiming
 * @Platform: Mac
 * @Date: 5:14 下午 2020/9/25
 *
 * 代碼優(yōu)化:支付寶小程序支付優(yōu)化成通過工廠,策略柒傻,模版 3種模式實現(xiàn)
 * 此接口為:訂單支付的策略接口
 */
public interface IOrderAlipayStrategy {

    /**
     * @param alipayConfig
     * @param payType
     * @param orderId
     * @param alipayUserId
     * @return
     *
     * 通過alipay支付孝赫,OrderInfo,包含阿里支付返回的TradeNo
     */
    OrderInfo payThroughAlipay(AlipayConfig alipayConfig,  PayType payType, String orderId, String alipayUserId) throws Exception;
}

OrderInfo用戶存放模版方法返回的內(nèi)容,代碼如下红符。

public class OrderInfo {
    private String payOrderId;
    private BigDecimal totalAmount;
    private String subject;
    private String tradeNo;

    public OrderInfo(BigDecimal totalAmount, String subject, PayType payType, String orderNo) {
        this.payOrderId = String.join("-", payType.getCode(), orderNo);
        this.totalAmount = totalAmount;
        this.subject = subject;
    }

    // 省略其他getter青柄,setter
}
  1. 創(chuàng)建抽象模版類伐债,實現(xiàn)IOrderAlipayStrategy接口,并且實現(xiàn)payThroughAlipay方法致开,可以從代碼中看到峰锁,payThroughAlipay里的1,2双戳,3虹蒋,4,5飒货,這5個步驟中魄衅,只有1,5塘辅,是與業(yè)務(wù)相關(guān)的晃虫,2,3扣墩,4是與業(yè)務(wù)無關(guān)的傲茄。那么該方法中年的2,3沮榜,4步驟的代碼就可以在該模版類中實現(xiàn)盘榨,并且由該方法調(diào)用。1蟆融,5步驟因為是與業(yè)務(wù)相關(guān)草巡,也就是說對于不同的訂單類型,實現(xiàn)不同型酥,那么1山憨,5步驟的方法在模版類中就寫成抽象方法,具體實現(xiàn)推遲到繼承模版方法的各個策略類中弥喉。當(dāng)然某些場景下1郁竟,5步驟也可以寫成默認實現(xiàn),具體策略類對默認實現(xiàn)不滿意的話由境,可以覆蓋默認實現(xiàn)棚亩。
/**
 * @Author: yesiming
 * @Platform: Mac
 * @Date: 5:28 下午 2020/9/25
 */
public abstract class AbstractOrderAlipayStrategy implements IOrderAlipayStrategy {

    @Value("${url.domain.name}")
    public String DOMAIN_NAME;

    @Autowired
    private AlipayService alipayService;

    @Override
    public OrderInfo payThroughAlipay(AlipayConfig alipayConfig, PayType payType, String orderId, String alipayUserId) throws Exception {
        // 1. 準(zhǔn)備參數(shù),業(yè)務(wù)相關(guān)
        OrderInfo orderInfo = prepareArgs(orderId, payType);

        // 2. 組裝DTO對象
        AlipayTradeCreateBizContentDTO bizContentInputDTO =
                assembleAlipayTradeCreateBizContentDTO(alipayUserId, orderInfo);

        // 3. 調(diào)用阿里支付
        String tradeNo = realPayThroughAlipay(alipayConfig,
                bizContentInputDTO,
                DOMAIN_NAME + AlipayConstants.NOTIFY_MINIPROGRAM_PAYCALLBACK);

        // 4. 附加對TradeNo的處理
        attachTradeNo(orderInfo, tradeNo);

        // 5. 后續(xù)處理虏杰,業(yè)務(wù)相關(guān)
        followUp(tradeNo);
        return orderInfo;
    }

    /**
     * 業(yè)務(wù)相關(guān)讥蟆,返回不同Order類型
     * @param orderId
     * @return
     */
    public abstract OrderInfo prepareArgs(String orderId, PayType payType);

    /**
     * 組裝DTO
     * 業(yè)務(wù)無關(guān),模版實現(xiàn)
     */
    private AlipayTradeCreateBizContentDTO assembleAlipayTradeCreateBizContentDTO(String alipayUserId,
                                                OrderInfo orderInfo) {

        AlipayTradeCreateBizContentDTO bizContentInputDTO = new AlipayTradeCreateBizContentDTO();
        bizContentInputDTO.setBuyerId(alipayUserId);
        bizContentInputDTO.setOutTradeNo(orderInfo.getPayOrderId());
        bizContentInputDTO.setTotalAmount(orderInfo.getTotalAmount());
        bizContentInputDTO.setSubject(orderInfo.getSubject());

        return bizContentInputDTO;
    }

    /**
     * 調(diào)用Alipay支付
     * 業(yè)務(wù)無關(guān)纺阔,模版實現(xiàn)
     */
    private String realPayThroughAlipay(AlipayConfig alipayConfig,
                     AlipayTradeCreateBizContentDTO bizContentInputDTO,
                     String notifyUrl) throws Exception {
        String tradeNo = alipayService.getTradeNo(alipayConfig, bizContentInputDTO,
                DOMAIN_NAME + AlipayConstants.NOTIFY_MINIPROGRAM_PAYCALLBACK);
        return tradeNo;
    }

    /**
     * 處理tradeNo返回值
     * 業(yè)務(wù)無關(guān)瘸彤,模版實現(xiàn)
     */
    private void attachTradeNo(OrderInfo orderInfo, String tradeNo) {
        orderInfo.setTradeNo(tradeNo);
    }

    /**
     * 業(yè)務(wù)相關(guān),自定義后續(xù)處理笛钝,
     * 如果需要后續(xù)處理可以重寫此方法
     *
     * @param tradeNo
     */
    public void followUp(String tradeNo) {}
}
  1. 到此為止质况,已經(jīng)把優(yōu)化之前的每個case分支里的流程框架寫完了愕宋,接下來只需要完成每個case分支的策略類即可。
  2. 策略類需要繼承模版類结榄,并且重寫模版類中的業(yè)務(wù)相關(guān)方法中贝。
    4.1 策略類:員工訂單服務(wù)費
/**
 * @Author: yesiming
 * @Platform: Mac
 * @Date: 5:13 下午 2020/9/25
 *
 * 員工訂單服務(wù)費
 */
@Component
public class OrgOrderServiceStrategy extends AbstractOrderAlipayStrategy {
    
    @Autowired
    private OrgOrderMapper orgOrderMapper;

    /**
     * 重寫了模版類中的業(yè)務(wù)相關(guān)方法,實現(xiàn)業(yè)務(wù)的獨立性
     * @param orderId
     * @param payType
     * @return
     */
    @Override
    public OrderInfo prepareArgs(String orderId, PayType payType) {
        OrgOrder orgOrderService = orgOrderMapper.getById(Long.parseLong(orderId));
        BigDecimal payAmount = orgOrderService.getTotalServiceCostAmount();
        OrderInfo orderInfo = new OrderInfo(payAmount,orgOrderService.getTypeName(), payType, orgOrderService.getOrderNo());
        return orderInfo;
    }
}

4.2 策略類:員工訂單商品服務(wù)費

@Component
public class OrgOrderObjStrategy extends AbstractOrderAlipayStrategy {

    @Autowired
    private OrgOrderMapper orgOrderMapper;

    @Override
    public OrderInfo prepareArgs(String orderId, PayType payType) {
        OrgOrder orgOrderService = orgOrderMapper.getById(Long.parseLong(orderId));
        BigDecimal payAmount = orgOrderService.getObjCostAmount();
        OrderInfo orderInfo = new OrderInfo(payAmount,orgOrderService.getTypeName(), payType, orgOrderService.getOrderNo());
        return orderInfo;
    }
}

4.3. 策略類:活動訂單

@Component
public class ActivityOrderStrategy extends AbstractOrderAlipayStrategy {

    @Autowired
    private ConfigurableActivityService configurableActivityService;

    @Autowired
    private ActivityOwnerMapper activityOwnerMapper;

    /**
     * 簡化方法之間的數(shù)據(jù)傳遞
     */
    private ThreadLocal<Long> activityOwnerIdTL = new ThreadLocal<>();

    @Override
    public OrderInfo prepareArgs(String orderId, PayType payType) {

        ActivityOwner activityOwner = configurableActivityService.getActivityOwnerById(Long.parseLong(orderId));
        activityOwnerIdTL.set(activityOwner.getId());
        BigDecimal payAmount = activityOwner.getApplyMoney();
        OrderInfo orderInfo = new OrderInfo(
                payAmount, // 支付金額
                WechatConstants.orderBody, //
                payType, // 支付類型
                activityOwner.getId().toString() + "-" + System.currentTimeMillis());

        return orderInfo;
    }

    /**
     * 后續(xù)處理潭陪,
     * 需要prepareArgs()方法執(zhí)行過程中的數(shù)據(jù)雄妥,還不想通過返回值來傳遞
     * 可以通過ThreadLocal來完成
     *
     * @param tradeNo
     */
    @Override
    public void followUp(String tradeNo) {
        if (tradeNo != null) {
            ActivityOwner activityOwner = new ActivityOwner();
            activityOwner.setId(activityOwnerIdTL.get());
            activityOwner.setSource("mini_alipay");
            activityOwnerMapper.updateSource(activityOwner);
        }
    }
}

4.4 還有很多策略類不寫了最蕾,從上面給出的3個策略類的代碼看依溯,前2個策略類,只實現(xiàn)了prepareArgs方法瘟则,沒有實現(xiàn)followUp方法黎炉,那么對于這2個策略類來說,followUp的處理將使用模版類里提供的默認處理方式醋拧,也就是什么都不做慷嗜,第三個策略類重寫了followUp方法,那么它將使用自己的重寫邏輯丹壕。

  1. 策略類寫完了庆械,下面就可以在service里調(diào)用了【担可是這么多策略類如何調(diào)用呢缭乘,怎么知道調(diào)用哪一個呢?可以將switch case放到一個工廠類里面琉用。需要注意的是堕绩,使用spring框架時,這里一定要注入需要用到的策略類邑时,不能在case里new奴紧,new的話會導(dǎo)致策略類里的屬性不被填充。
    ---------- 這里有個小小的坑晶丘,請看后文【后記】部分 ----------
/**
 * @Author: yesiming
 * @Platform: Mac
 * @Date: 10:16 下午 2020/9/25
 */
@Component
public class OrderStrategyFactory {

    @Autowired
    private ActivityOrderStrategy activityOrderStrategy;

    @Autowired
    private GroupbuyOrderStrategy groupbuyOrderStrategy;

    @Autowired
    private OrgOrderObjStrategy orgOrderObjStrategy;

    @Autowired
    private OrgOrderRewardStrategy orgOrderRewardStrategy;

    @Autowired
    private OrgOrderServiceStrategy orgOrderServiceStrategy;

    @Autowired
    private ShopOrderStrategy shopOrderStrategy;

    @Autowired
    private ShopVoucherStrategy shopVoucherStrategy;

    @Autowired
    private YxOrderStrategy yxOrderStrategy;

    public IOrderAlipayStrategy createOrderInstannce(PayType payType) {

        IOrderAlipayStrategy orderAlipayStrategy = null;
        switch (payType) { // TODO: 這里需要增加一個策略工廠類
            case ORG_ORDER_SERVICE: // DONE
                orderAlipayStrategy = orgOrderServiceStrategy;
                break;
            case ORG_ORDER_OBJ: // DONE
                orderAlipayStrategy = orgOrderObjStrategy;
                break;
            case ORG_ORDER_REWAED: // DONE
                orderAlipayStrategy = orgOrderRewardStrategy;
                break;
            case GROUPBUY_ORDER: // DONE
                orderAlipayStrategy = groupbuyOrderStrategy;
                break;
            case SHOP_ORDER: // DONE
                orderAlipayStrategy = shopOrderStrategy;
                break;
            case SHOP_VOUCHER: // DONE
                orderAlipayStrategy = shopVoucherStrategy;
                break;
            case YX_ORDER: // DONE
                orderAlipayStrategy = yxOrderStrategy;
                break;
            case PROPERTY_FEE:
//                PropertyCostGd propertyCostGd = propertyCostsGdMapper.selPropertyCostGdByApplyId(orderId);
//                OwnerAccountPropertyGd ownerAccountPropertyGd = getPropertyFee(alipayConfig, domainName, payOrderId, alipayUserId, propertyCostGd);
//                payOrderId = String.join("-", PayType.PROPERTY_FEE.getCode(), ownerAccountPropertyGd.getNumber());
//                tradeNo = ownerAccountPropertyGd.getPaymentPlatformBillCode();
//                break;
            case ACTIVITY_ORDER: // DONE
                orderAlipayStrategy = activityOrderStrategy;
                break;
            default:
        }

        return orderAlipayStrategy;
    }

}
  1. 有了工廠類黍氮,那么現(xiàn)在就能在service里調(diào)用了。
    為了看出來區(qū)別浅浮,優(yōu)化前的那一大段switch case我保留下來了滤钱。
    @Override
    public String getPayInfo(AlipayConfig alipayConfig, String domainName, PayType payType, String orderId, String alipayUserId) throws Exception {
//        switch (payType) { // TODO: 這里需要增加一個策略工廠類
//            case ORG_ORDER_SERVICE: // DONE
//                OrgOrder orgOrderService = orgOrderMapper.getById(Long.parseLong(orderId));
//                payAmount = orgOrderService.getTotalServiceCostAmount();
//                payOrderId = String.join("-", payType.getCode(), orgOrderService.getOrderNo());
//                tradeNo = getOrgOrderServicePrepayId(alipayConfig, domainName, payOrderId, alipayUserId, orgOrderService);
//                break;
//            case ORG_ORDER_OBJ: // DONE
//                OrgOrder orgOrderObj = orgOrderMapper.getById(Long.parseLong(orderId));
//                payAmount = orgOrderObj.getObjCostAmount();
//                payOrderId = String.join("-", payType.getCode(), orgOrderObj.getOrderNo());
//                tradeNo = getOrgOrderObjPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, orgOrderObj);
//                break;
//            case ORG_ORDER_REWAED: // DONE
//                OrgOrder orgOrderReward = orgOrderMapper.getById(Long.parseLong(orderId));
//                payAmount = orgOrderReward.getRewardAmount();
//                payOrderId = String.join("-", payType.getCode(), orgOrderReward.getOrderNo(), Long.toString(System.currentTimeMillis()));
//                tradeNo = getOrgOrderRewardPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, orgOrderReward);
//                break;
//            case GROUPBUY_ORDER: // DONE
//                PrivilegeGroupbuyingOrder privilegeGroupbuyingOrder = privilegeGroupbuyingOrderMapper.getById(Long.parseLong(orderId));
//                payAmount = privilegeGroupbuyingOrder.getTotalAmount();
//                payOrderId = String.join("-", payType.getCode(), privilegeGroupbuyingOrder.getNumber());
//                tradeNo = getGroupbuyAlipayPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, privilegeGroupbuyingOrder);
//                break;
//            case SHOP_ORDER: // DONE
//                PrivilegeShopsOrder privilegeShopsOrder = privilegeShopsOrderMapper.getById(Long.parseLong(orderId));
//                payAmount = privilegeShopsOrder.getTotalAmount();
//                payOrderId = String.join("-", payType.getCode(), privilegeShopsOrder.getNumber());
//                tradeNo = getShopOrderAlipayPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, privilegeShopsOrder);
//                break;
//            case SHOP_VOUCHER: // DONE
//                PrivilegeShopsVoucher privilegeShopsVoucher = privilegeShopsVoucherMapper.getById(Long.parseLong(orderId));
//                payAmount = privilegeShopsVoucher.getTotalAmount();
//                payOrderId = String.join("-", payType.getCode(), privilegeShopsVoucher.getNumber());
//                tradeNo = getShopVoucherAlipayPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, privilegeShopsVoucher);
//                break;
//            case YX_ORDER: // DONE
//                YxOrder yxOrder = yxOrderMapper.getYxOrder(orderId);
//                payAmount = yxOrder.getRealPrice();
//                payOrderId = String.join("-", payType.getCode(), yxOrder.getOrderId());
//                tradeNo = getYxOrderAlipayPrepayId(alipayConfig, domainName, payOrderId, alipayUserId, yxOrder);
//                break;
//            case PROPERTY_FEE:
////                PropertyCostGd propertyCostGd = propertyCostsGdMapper.selPropertyCostGdByApplyId(orderId);
////                OwnerAccountPropertyGd ownerAccountPropertyGd = getPropertyFee(alipayConfig, domainName, payOrderId, alipayUserId, propertyCostGd);
////                payOrderId = String.join("-", PayType.PROPERTY_FEE.getCode(), ownerAccountPropertyGd.getNumber());
////                tradeNo = ownerAccountPropertyGd.getPaymentPlatformBillCode();
////                break;
//            case ACTIVITY_ORDER: // DONE
//                ActivityOwner activityOwner = configurableActivityService.getActivityOwnerById(Long.parseLong(orderId));
//                payAmount = activityOwner.getApplyMoney();
//                payOrderId = String.join("-", PayType.ACTIVITY_ORDER.getCode(), activityOwner.getId().toString(), System.currentTimeMillis()+"");
//                tradeNo = getActivitePrepayId(alipayConfig, domainName, payOrderId, alipayUserId, activityOwner);
//                break;
//            default:
//        }

        // 工廠根據(jù)payType創(chuàng)建并返回對應(yīng)的策略類。
        IOrderAlipayStrategy orderAlipayStrategy = orderStrategyFactory.createOrderInstannce(payType);
        // 策略類去執(zhí)行模版方法
        OrderInfo orderInfo = orderAlipayStrategy.payThroughAlipay(alipayConfig, payType, orderId, alipayUserId);

        savePayLog(payType, orderId, orderInfo.getPayOrderId(), PayChannel.ALI, orderInfo.getTradeNo(), alipayUserId, orderInfo.getTotalAmount());
        return orderInfo.getTradeNo();
    }
  1. 優(yōu)化完成脑题,看一下增加的類與接口
    004

總結(jié)

對于大部分業(yè)務(wù)代碼件缸,遇到這種業(yè)務(wù)場景應(yīng)該也不少,而模版方法與策略模式的結(jié)合正是用來解決這種場景的利器叔遂,當(dāng)然他炊,當(dāng)策略特別多的時候也會導(dǎo)致其他問題争剿,比如類爆炸,不過那是另一個話題了痊末。

后記

以上的代碼蚕苇,基本上實現(xiàn)了目標(biāo),提取出通用代碼凿叠,各個策略類各司其職只專注于自身特性的業(yè)務(wù)代碼涩笤,通過工廠類產(chǎn)生需要的業(yè)務(wù)類對象。
但是盒件,有一個缺陷:工廠類蹬碧,工廠類通過switch case語句來產(chǎn)生策略類對象,如果訂單類型增加了炒刁,也就需要策略類恩沽,工廠也就需要增加對新策略類的支持。
這時就需要修改工廠類翔始,修改類容如下:
1. 增加新的策略類屬性罗心;
2. 增加case語句。
好的城瞎,問題來了:開閉原則被打破了渤闷!
如何繼續(xù)優(yōu)化這部分呢?請看《寫出優(yōu)雅的業(yè)務(wù)代碼(2):優(yōu)化掉工廠模式中的 switch case》

最后附上uml圖
004
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脖镀,一起剝皮案震驚了整個濱河市飒箭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌认然,老刑警劉巖补憾,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卷员,居然都是意外死亡盈匾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門毕骡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來削饵,“玉大人,你說我怎么就攤上這事未巫×耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵叙凡,是天一觀的道長劈伴。 經(jīng)常有香客問我,道長握爷,這世上最難降的妖魔是什么跛璧? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任严里,我火速辦了婚禮,結(jié)果婚禮上追城,老公的妹妹穿的比我還像新娘刹碾。我一直安慰自己,他們只是感情好座柱,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布迷帜。 她就那樣靜靜地躺著,像睡著了一般色洞。 火紅的嫁衣襯著肌膚如雪戏锹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天锋玲,我揣著相機與錄音景用,去河邊找鬼涵叮。 笑死惭蹂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的割粮。 我是一名探鬼主播盾碗,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舀瓢!你這毒婦竟也來了廷雅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤京髓,失蹤者是張志新(化名)和其女友劉穎航缀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堰怨,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡芥玉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了备图。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灿巧。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖揽涮,靈堂內(nèi)的尸體忽然破棺而出抠藕,到底是詐尸還是另有隱情,我是刑警寧澤蒋困,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布盾似,位于F島的核電站,受9級特大地震影響雪标,放射性物質(zhì)發(fā)生泄漏零院。R本人自食惡果不足惜购岗,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望门粪。 院中可真熱鬧喊积,春花似錦、人聲如沸玄妈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拟蜻。三九已至绎签,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酝锅,已是汗流浹背诡必。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搔扁,地道東北人爸舒。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像稿蹲,于是被迫代替她去往敵國和親扭勉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354