手摸手,使用Dart語言開發(fā)后端應用噪馏,來吧刊懈!

前言

這幾天連續(xù)發(fā)了幾篇關于 Dart 開發(fā)后端應用的文章,主要是介紹了 Dart 的一些優(yōu)點捷凄,比如異步任務忱详,并發(fā)處理,編譯部署等等跺涤。

俗話說匈睁,光說不練假把式,今天我們來真正開始一個 Dart 后端應用桶错。

我們要開發(fā)什么應用

假設我們現(xiàn)在要開發(fā)一個社區(qū)應用航唆,類似于掘金CSDN等等院刁,基本的功能是用戶發(fā)文章糯钙,發(fā)觀點。

發(fā)文章退腥,類似于傳統(tǒng)的CMS系統(tǒng)

發(fā)觀點任岸,類似于現(xiàn)在的微博系統(tǒng)

圍繞核心,還有標簽狡刘,分類享潜,評論等等。

我們用什么框架

既然打算使用 Dart 開發(fā)颓帝,有個開發(fā)框架還是有很大幫助的米碰。 然而 Dart 的后端框架并不多,aqueduct, jaguar, DartMars 等等, 在這里购城,我們使用 DartMars吕座。

源碼在此 https://github.com/tangpanqing/dart_mars

文檔在此 https://tangpanqing.github.io/dart_mars_docs/zh/

打開文檔首頁,如此

微信圖片_20210703095222.png

嗯嗯瘪板,濃濃的 vuepress 味道吴趴。

開始一個項目如此簡單

根據(jù)DartMars的指引,在安裝Dart 后侮攀,我們可以執(zhí)行以下命令來創(chuàng)建項目

# 安裝DartMars
dart pub global activate --source git https://github.com/tangpanqing/dart_mars.git

# 創(chuàng)建項目
dart pub global run dart_mars --create project_name

# 進入目錄
cd project_name

# 獲取依賴
dart pub global run dart_mars --get 

# 啟動項目
dart pub global run dart_mars --serve dev

手摸手锣枝,我們一步一步來

第一步厢拭,安裝DartMars

打開命令行工具,執(zhí)行

dart pub global activate --source git https://github.com/tangpanqing/dart_mars.git

感謝墻的存在撇叁,我等了將近1分鐘供鸠,提示我如下:

Activated dart_mars 1.0.4 from Git repository "https://github.com/tangpanqing/dart_mars.git"

這就表示安裝好了。

第二步陨闹,創(chuàng)建項目

項目暫定名稱 community 社區(qū)楞捂,執(zhí)行如下命令

dart pub global run dart_mars --create community

經(jīng)過以上命令,DartMars 有了提示

project community has been created
you can change dir with command: cd community
and then get dependent with command: dart pub global run dart_mars --get
and then start it with command: dart pub global run dart_mars --serve dev

意思說趋厉,項目已經(jīng)創(chuàng)建寨闹,接下來你需要進入目錄,并且獲取依賴君账,最后執(zhí)行繁堡。

并且顯示了相關命令,是不是很貼心乡数? 談戀愛的時候椭蹄,一定是個暖男。

第三步瞳脓,進入目錄

執(zhí)行命令

cd community

第四步塑娇,獲取依賴

執(zhí)行命令

dart pub global run dart_mars --get

經(jīng)過以上命令,DartMars 有了提示

Got dependencies!

表示加載依賴完成

第五步劫侧,啟動項目

dart pub global run dart_mars --serve dev

經(jīng)過以上命令埋酬,DartMars 有了提示

route config file has been updated, see ./lib/config/route.dart
$ dart run bin\community.dart --serve dev
INFO::2021-07-03 10:14:13.601023::0::Server::Http Server has start, port=80
INFO::2021-07-03 10:14:13.608004::1::Server::Env type is dev
INFO::2021-07-03 10:14:13.624571::2::Server::Open browser and vist http://127.0.0.1:80 , you can see some info

啟動成功,通過以上信息烧栋,我們可知:

  1. 路由配置文件已經(jīng)更新写妥,

  2. HTTP 服務已經(jīng)開始,在80端口审姓,目前使用的是開發(fā)環(huán)境

打開瀏覽器珍特,訪問 http://127.0.0.1:80 我們就看到了經(jīng)典的

hello world

按部就班地繼續(xù)編碼

先看一眼項目結構

微信圖片_20210703115930.png

bin 目錄是執(zhí)行文件的入口

lib 目錄是整個項目的開發(fā)目錄

其他目錄都是一些輔助性的,如名字所示魔吐。接下來扎筒,我們要按部就班的完成基本功能。

先完成第一個酬姆,用戶的增查改刪嗜桌,并且做成標準,以后使用辞色。

創(chuàng)建用戶表

我已經(jīng)提前準備好了相關的sql 語句

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(40) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶ID',
  `user_mobile` varchar(11) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶手機號',
  `user_password` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶密碼',
  `user_nickname` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶昵稱',
  `user_avatar` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶頭像',
  `user_description` varchar(120) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶介紹',
  `create_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '創(chuàng)建時間',
  `update_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '更新時間',
  `delete_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '刪除時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`),
  KEY `user_mobile` (`user_mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用戶表';

放到mysql 去執(zhí)行

創(chuàng)建用戶模型

用戶模型用來與數(shù)據(jù)表進行對應的骨宠,方便面向對象開發(fā)。
在目錄 lib/extend/model/ 下,新建模型文件 User.dart层亿,鍵入如下內(nèi)容

class User {
  int id;
  String userId;
  String userMobile;
  String userPassword;
  String userNickname;
  String userAvatar;
  String userDescription;
  int createTime;
  int updateTime;
  int deleteTime;
}

這里只是定義了類名桦卒,以及相關屬性,還需要補充一些方法匿又。補充模型類的方法方灾,是一個枯燥的事情,建議使用工具琳省。

如果你使用的是 VSCode迎吵,并且安裝了 Dart Data Class Generator 插件躲撰,此時點擊類名针贬,將會出現(xiàn)幫助,點擊下圖紅色框框內(nèi)拢蛋,將補充完成代碼桦他。

微信圖片_20210703120712.png

我們將得到以下結果

import 'dart:convert';

class User {
  int id;
  String userId;
  String userMobile;
  String userPassword;
  String userNickname;
  String userAvatar;
  String userDescription;
  int createTime;
  int updateTime;
  int deleteTime;

  User({
    this.id,
    this.userId,
    this.userMobile,
    this.userPassword,
    this.userNickname,
    this.userAvatar,
    this.userDescription,
    this.createTime,
    this.updateTime,
    this.deleteTime,
  });

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'userId': userId,
      'userMobile': userMobile,
      'userPassword': userPassword,
      'userNickname': userNickname,
      'userAvatar': userAvatar,
      'userDescription': userDescription,
      'createTime': createTime,
      'updateTime': updateTime,
      'deleteTime': deleteTime,
    };
  }

  factory User.fromMap(Map<String, dynamic> map) {
    return User(
      id: map['id'],
      userId: map['userId'],
      userMobile: map['userMobile'],
      userPassword: map['userPassword'],
      userNickname: map['userNickname'],
      userAvatar: map['userAvatar'],
      userDescription: map['userDescription'],
      createTime: map['createTime'],
      updateTime: map['updateTime'],
      deleteTime: map['deleteTime'],
    );
  }

  String toJson() => json.encode(toMap());

  factory User.fromJson(String source) => User.fromMap(json.decode(source));

  @override
  String toString() {
    return 'User(id: $id, userId: $userId, userMobile: $userMobile, userPassword: $userPassword, userNickname: $userNickname, userAvatar: $userAvatar, userDescription: $userDescription, createTime: $createTime, updateTime: $updateTime, deleteTime: $deleteTime)';
  }
}

經(jīng)過剛才的操作,可以看到

多了三個實例化函數(shù) User, User.fromMap, User.fromJson

多了三個方法 toMap, toJson, toString

為什么要做這些谆棱,歸根到底是因為 Dart 禁用反射快压,當我們從其他地方拿到數(shù)據(jù),無法直接轉成模型對象垃瞧。只能先轉成map蔫劣,或者json字符串,然后再手工轉成模型對象个从。

是稍稍復雜了點脉幢,為了更好的性能,不算大問題嗦锐。

創(chuàng)建服務

服務用來處理實際業(yè)務嫌松,被控制器所調(diào)用。

在目錄 lib/extend/service/ 下奕污,新建服務文件 UserService.dart萎羔,鍵入如下內(nèi)容

import 'package:community/bootstrap/db/Db.dart';
import 'package:community/bootstrap/db/DbColumn.dart';
import 'package:community/bootstrap/helper/ConvertHelper.dart';
import 'package:community/extend/helper/PasswordHelper.dart';
import 'package:community/extend/helper/TimeHelper.dart';
import 'package:community/extend/helper/UniqueHelper.dart';
import 'package:community/extend/model/Page.dart';
import 'package:community/extend/model/User.dart';

class UserService {
  static String _table = "user";

  /// 分頁查詢
  static Future<Page<User>> query(
      List<DbColumn> condition, int pageNum, int pageSize) async {
    int totalCount = await Db(_table).where(condition).count('*');

    List<Map<String, dynamic>> mapList = await Db(_table)
        .where(condition)
        .page(pageNum, pageSize)
        .order("create_time desc")
        .select();

    List<User> list =
        mapList.map((e) => User.fromMap(ConvertHelper.keyToHump(e))).toList();

    return Page<User>(totalCount, pageNum, pageSize, list);
  }

  /// 根據(jù)用戶ID查詢
  static Future<User> findById(String userId) async {
    List<DbColumn> where = [
      DbColumn.fieldToUnderLine("userId", "=", userId),
      DbColumn.fieldToUnderLine("deleteTime", "=", 0),
    ];

    Map<String, dynamic> map = await Db(_table).where(where).find();
    if (null == map) throw "沒有找到用戶";

    return User.fromMap(ConvertHelper.keyToHump(map));
  }

  /// 添加用戶
  static Future<User> add(
    String userMobile,
    String userPassword,
    String userNickname,
    String userAvatar,
    String userDescription,
  ) async {
    Map<String, dynamic> userMap = await _findByMobile(userMobile);

    if (null != userMap) throw '該手機號已存在';

    User user = User(
        userId: UniqueHelper.userId(),
        userMobile: userMobile,
        userPassword: PasswordHelper.password(userPassword),
        createTime: TimeHelper.timestamp(),
        userNickname: userNickname,
        userAvatar: userAvatar,
        userDescription: userDescription,
        updateTime: 0,
        deleteTime: 0);

    user.id = await Db(_table).insert(ConvertHelper.keyToUnderLine(user.toMap()));

    return user;
  }

  /// 修改用戶昵稱
  static Future<User> updateNickname(String userId, String userNickname) async {
    User user = await findById(userId);
    user.userNickname = userNickname;

    await _updateField(user.toMap(), 'userId', ['userNickname']);

    return user;
  }

  /// 根據(jù)用戶ID刪除,軟刪除
  static Future<User> delete(String userId) async {
    User user = await findById(userId);
    user.deleteTime = TimeHelper.timestamp();

    await _updateField(user.toMap(), 'userId', ['deleteTime']);

    return user;
  }

  /// 根據(jù)用戶手機號查詢
  static Future<Map<String, dynamic>> _findByMobile(String userMobile) async {
    List<DbColumn> condition = [
      DbColumn.fieldToUnderLine("userMobile", "=", userMobile),
      DbColumn.fieldToUnderLine("deleteTime", "=", 0),
    ];

    Map<String, dynamic> map = await Db(_table).where(condition).find();

    return map;
  }

  /// 更新表字段
  static Future<int> _updateField(
      Map<String, dynamic> map, String keyName, List<String> fieldList) async {
    List<DbColumn> condition = [
      DbColumn.fieldToUnderLine(keyName, '=', map[keyName])
    ];

    Map<String, dynamic> updateMap = {};
    fieldList.forEach((fieldName) {
      updateMap[fieldName] = map[fieldName];
    });

    return await Db(_table)
        .where(condition)
        .update(ConvertHelper.keyToUnderLine(updateMap));
  }
}

上述代碼,是對數(shù)據(jù)的增查改刪碳默,和其他語言的代碼贾陷,大同小異,一些容易迷惑的地方嘱根,稍微解釋下髓废。

在分頁查詢中

List<User> list =
        mapList.map((e) => User.fromMap(ConvertHelper.keyToHump(e))).toList();

這里主要的作用是,將 mapList 這個鍵值對的列表儿子,轉換成 User 對象列表瓦哎。

另外,因為我們數(shù)據(jù)庫的字段名是下劃線格式的,而模型類的屬性是駝峰格式的蒋譬,所以需要一個轉換過程割岛。

ConvertHelper.keyToHump 的作用是將鍵名為 下劃線格式 的鍵值對,轉換成鍵名為 駝峰格式 的鍵值對犯助。

創(chuàng)建控制器

控制器用于接收用戶請求參數(shù)癣漆,并調(diào)用服務來處理業(yè)務,最后返回信息

在目錄 lib/app/controller/ 下剂买,新建模型文件 UserController.dart惠爽,鍵入如下內(nèi)容

import 'package:community/bootstrap/Context.dart';
import 'package:community/bootstrap/db/DbColumn.dart';
import 'package:community/bootstrap/db/DbTrans.dart';
import 'package:community/bootstrap/helper/VerifyHelper.dart';
import 'package:community/bootstrap/meta/RouteMeta.dart';
import 'package:community/extend/model/Page.dart';
import 'package:community/extend/model/User.dart';
import 'package:community/extend/service/UserService.dart';

class UserController {
  @RouteMeta('/home/user/query', 'GET|POST')
  static void query(Context ctx) async {
    int pageNum = ctx.getPositiveInt('pageNum', def: 1);
    int pageSize = ctx.getPositiveInt('pageSize', def: 20);

    await DbTrans.simple(ctx, () async {
      List<DbColumn> condition = [];
      Page<User> res = await UserService.query(condition, pageNum, pageSize);
      ctx.showSuccess('已獲取', res.toMap());
    });
  }

  @RouteMeta('/home/user/findById', 'GET|POST')
  static void findById(Context ctx) async {
    String userId = ctx.getString('userId');
    if (VerifyHelper.empty(userId)) return ctx.showError('用戶ID不能為空');

    await DbTrans.simple(ctx, () async {
      User res = await UserService.findById(userId);
      ctx.showSuccess('已獲取', res.toMap());
    });
  }

  @RouteMeta('/home/user/add', 'GET|POST')
  static void add(Context ctx) async {
    String userMobile = ctx.getString('userMobile');
    String userPassword = ctx.getString('userPassword');
    String userNickname = ctx.getString('userNickname');
    String userAvatar = ctx.getString('userAvatar');
    String userDescription = ctx.getString('userDescription');

    if (VerifyHelper.empty(userMobile)) return ctx.showError('用戶手機號不能為空');
    if (VerifyHelper.empty(userPassword)) return ctx.showError('用戶密碼不能為空');
    if (VerifyHelper.empty(userNickname)) return ctx.showError('用戶昵稱不能為空');
    if (VerifyHelper.empty(userAvatar)) return ctx.showError('用戶頭像不能為空');
    if (VerifyHelper.empty(userDescription)) return ctx.showError('用戶描述不能為空');

    await DbTrans.simple(ctx, () async {
      User res = await UserService.add(
          userMobile, userPassword, userNickname, userAvatar, userDescription);
      ctx.showSuccess('已添加', res.toMap());
    });
  }

  @RouteMeta('/home/user/updateNickname', 'GET|POST')
  static void updateNickname(Context ctx) async {
    String userId = ctx.getString('userId');
    String userNickname = ctx.getString('userNickname');
    if (VerifyHelper.empty(userId)) return ctx.showError('用戶ID不能為空');
    if (VerifyHelper.empty(userNickname)) return ctx.showError('用戶昵稱不能為空');

    await DbTrans.simple(ctx, () async {
      User res = await UserService.updateNickname(userId, userNickname);
      ctx.showSuccess('已更改', res.toMap());
    });
  }

  @RouteMeta('/home/user/delete', 'GET|POST')
  static void delete(Context ctx) async {
    String userId = ctx.getString('userId');
    if (VerifyHelper.empty(userId)) return ctx.showError('用戶ID不能為空');

    await DbTrans.simple(ctx, () async {
      User res = await UserService.delete(userId);
      ctx.showSuccess('已刪除', res.toMap());
    });
  }
}

有必要說明一下:

RouteMetaDartMars 定義的路由元數(shù)據(jù),類似于java 里的注解瞬哼。

相同的作用是婚肆,可以對代碼進行描述,讓開發(fā)者知道所描述的代碼的功能坐慰。

不同的是较性,因為 DartMars 沒有反射,所以程序不能在運行的時候獲取元數(shù)據(jù)或者說注解的信息结胀,也就無法完成類似于java里注解生成代碼的功能赞咙。

當然,既然運行的時候不能生成代碼糟港,我們另尋他圖攀操,在編譯之前生成即可。

自動更新路由配置

接下來秸抚,我們啟動項目速和,執(zhí)行如下命令:

dart pub global run dart_mars --serve dev

請注意,控制臺打印的有這樣一句話

route config file has been updated, see ./lib/config/route.dart

說路由配置文件已經(jīng)更新耸别,地址是 ./lib/config/route.dart健芭,我們看看去

import '../bootstrap/helper/RouteHelper.dart';
import '../app/controller/HomeController.dart' as app_controller_HomeController;
import '../app/controller/UserController.dart' as app_controller_UserController;

/// 
/// don't modify this file yourself, this file content will be replace by DartMars
/// 
/// for more infomation, see doc about Route 
/// 
/// last replace time 2021-07-03 14:53:51.588722 
/// 
void configRoute(){
  RouteHelper.add('GET', '/', app_controller_HomeController.HomeController.index);
  RouteHelper.add('GET', '/user', app_controller_HomeController.HomeController.user);
  RouteHelper.add('GET', '/city/:cityName', app_controller_HomeController.HomeController.city);
  RouteHelper.add('GET|POST', '/home/user/query', app_controller_UserController.UserController.query);
  RouteHelper.add('GET|POST', '/home/user/findById', app_controller_UserController.UserController.findById);
  RouteHelper.add('GET|POST', '/home/user/add', app_controller_UserController.UserController.add);
  RouteHelper.add('GET|POST', '/home/user/updateNickname', app_controller_UserController.UserController.updateNickname);
  RouteHelper.add('GET|POST', '/home/user/delete', app_controller_UserController.UserController.delete);
}

果然,最后面添加了 5 個路由規(guī)則秀姐,和我們剛才在 UserController 里定義的一樣慈迈。

另外,如文件所提示的省有,這個文件不要手動更改痒留,當你運行 --serve 命令時, DartMars會自動更新。

測試接口

測試接口的工作非常簡單了蠢沿,可以使用專業(yè)工具伸头,也可以在瀏覽器中直接來。文章篇幅有限舷蟀,我就測試 2 個恤磷,其他的接口面哼,有興趣的同學自己來。

測試添加用戶接口

http://127.0.0.1/home/user/add?userMobile=18512345679&userPassword=123456&userNickname=tang&userAvatar=http://www.test.com/1.jpg&userDescription=test

返回如下

{
  "code": 200,
  "msg": "已添加",
  "data": {
    "id": 2,
    "userId": "1625295731292004882",
    "userMobile": "18512345679",
    "userPassword": "4616221982a9d1759d1d0cec7249a6d71da960d3",
    "userNickname": "tang",
    "userAvatar": "http://www.test.com/1.jpg",
    "userDescription": "test",
    "createTime": 1625295731,
    "updateTime": 0,
    "deleteTime": 0
  }
}

一切正常扫步,非常棒魔策。

測試查詢單個用戶接口

http://127.0.0.1/home/user/findById?userId=1625295731292004882

返回如下

{
  "code": 200,
  "msg": "已獲取",
  "data": {
    "id": 2,
    "userId": "1625295731292004882",
    "userMobile": "18512345679",
    "userPassword": "4616221982a9d1759d1d0cec7249a6d71da960d3",
    "userNickname": "tang",
    "userAvatar": "http://www.test.com/1.jpg",
    "userDescription": "test",
    "createTime": 1625295731,
    "updateTime": 0,
    "deleteTime": 0
  }
}

一切正常,非常棒河胎。

總結

能夠看到這里的同學闯袒,想必都是真愛了。

由上述流程走下來游岳,可以看出政敢,用 Dart 開發(fā)后端應用,與其他語言開發(fā)胚迫,并無太大的區(qū)別喷户。也說明一個事情,其他語言的開發(fā)者晌区,想轉用 Dart 開發(fā)后端應用程序摩骨,是一件很容易的事情。

加之 Dart 在客戶端開發(fā)領域的成功朗若, 一種語言完成客戶端與服務端絕對不再是夢想。

That's All, Enjoy.

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昌罩,一起剝皮案震驚了整個濱河市哭懈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茎用,老刑警劉巖遣总,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異轨功,居然都是意外死亡旭斥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門古涧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垂券,“玉大人,你說我怎么就攤上這事羡滑」阶Γ” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵柒昏,是天一觀的道長凳宙。 經(jīng)常有香客問我,道長职祷,這世上最難降的妖魔是什么氏涩? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任届囚,我火速辦了婚禮,結果婚禮上是尖,老公的妹妹穿的比我還像新娘奖亚。我一直安慰自己,他們只是感情好析砸,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布昔字。 她就那樣靜靜地躺著,像睡著了一般首繁。 火紅的嫁衣襯著肌膚如雪作郭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天弦疮,我揣著相機與錄音夹攒,去河邊找鬼。 笑死胁塞,一個胖子當著我的面吹牛咏尝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啸罢,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼编检,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扰才?” 一聲冷哼從身側響起允懂,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衩匣,沒想到半個月后蕾总,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡琅捏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年生百,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柄延。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚀浆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拦焚,到底是詐尸還是另有隱情蜡坊,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布赎败,位于F島的核電站秕衙,受9級特大地震影響,放射性物質發(fā)生泄漏僵刮。R本人自食惡果不足惜据忘,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一鹦牛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勇吊,春花似錦曼追、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至针史,卻和暖如春晶伦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啄枕。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工婚陪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人频祝。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓泌参,卻偏偏與公主長得像,于是被迫代替她去往敵國和親常空。 傳聞我的和親對象是個殘疾皇子沽一,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

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