小程序Lumen API開發(fā)

Lumen API開發(fā)

Lumen.png

Laravel和Lumen的區(qū)別:Lumen輕量級框架对扶,集合了Laravel的優(yōu)美語法执庐,支持ORM沒有Blade模板引擎,我個人覺得這個框架適用于API的開發(fā)虏冻,對于相比Laravel性能槽棍,如果Lumen打開了門面就缺失了它自己的特性,就和Laravel沒什么區(qū)別了汽纠。

安裝Lumen

  • 安裝Composer
cd /usr/local/bin

php -r“copy('https://getcomposer.org/installer'卫键,'composer-setup.php');”
php -r“if(hash_file('SHA384','composer-setup.php')==='544e09ee996cdf60ece3804abc52599c22f1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061'){echo'Installer verified';} else {echo'Installer corrupt'; unlink('composer-setup虱朵。 php');} echo PHP_EOL;“
php composer-setup.php
php -r“unlink('composer-setup.php');”

composer --version 或 composer -v  // 查看是否安裝成功莉炉?
  • Mac配置Path路徑
sudo vim ~/.bash_profile
export PATH=~/.composer/vendor/bin:$PATH  // 黏貼這段代碼
執(zhí)行source ~/.bash_profile                // 更新配置文件
  • 安裝Lumen項目(兩種方式)
第一種方式:通過 Lumen 安裝器 (不支持選擇版本默認最新的)
【幫助】lumen help new
【Lumen】lumen new project // 直接new一個項目就可以了

第二種方式:Composer 安裝(速度慢但可指定安裝版本)
【安裝指定版本】composer create-project laravel/lumen Lumen-5.5 --prefer-dist  "5.5.*"

// 目前只有通過 composer create-project 方式支持選擇版本, lumen new 方式不支持碴犬。

配置完域名直接訪問Lumen

Lumen-5.5.png

添加缺失的Artisan命令

執(zhí)行php artisan list會發(fā)現(xiàn)絮宁,Lumen相比Laravel缺失了一部分make···等命令,像我這種懶人必須得想辦法解決 gitHub:lumen-generators

not have-artisan.jpg
  • 執(zhí)行Composer安裝
composer require wn/lumen-generators
  • 注冊服務提供者Providers
// 在app/Providers/AppServiceProvider.php
public function register()
{
    if ($this->app->environment() == 'local') {
        $this->app->register('Wn\Generators\CommandsServiceProvider');
    }
}
  • 展示渲染Providers
// 在bootstrap/app.php找到Register Service Providers添加下面代碼
$app->register(Wn\Generators\CommandsServiceProvider::class);
  • 查看命令php artisan list
    wn-artisan.png
  • 修改Controller和Model模板源碼(不改你會后悔的7)


    templates-path.png

    控制器模板:

<?php

namespace App\Http\Controllers\{{name}};

use Laravel\Lumen\Routing\Controller;

class {{name}} extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Display a listing of the resource.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     * @author
     */
    public function index()
    {
        //
    }
}

model 模板:

<?php

namespace {{namespace}}\Models;

use Illuminate\Database\Eloquent\Model;

class {{name}} extends Model {

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = [];

}
  • wn配置完的artisan操作命令
// 有個bug需要自己去控制器里再編輯下名稱绍昂,著急開發(fā)懶得去翻源碼了
【生成控制器】php artisan wn:controller Static/Static

開啟Lumen的各種擴展必須組件 (門面操作,謹慎開啟)

// 開啟 Eloquent
$app->withEloquent();
// 開啟 Session
去掉 $app->middleware(); 的 StartSession 中間件的注釋
// 開啟 路由中間件
去掉 routeMiddleware 的注釋
// 配置 數(shù)據(jù)庫 偿荷,復制 /config文件
去源碼vendor/laravel/lumen-framework/config 復制到根目錄下
  • 對于Lumen的dd打印

什么窘游?哦,上帝跳纳!真是見鬼张峰!怎么會有人在 Laravel 中還在用 echo + die() ?好家伙棒旗,我敢打賭喘批,他一定沒有好好看文檔,我向圣母瑪利亞保證铣揉。如果讓我看到這群愚蠢的土撥鼠饶深,看在上帝的份上,我會用靴子狠狠地踢他們的屁股逛拱,我發(fā)誓我絕對會敌厘。 (來自一個逗比的Lumen dd() 打印解釋)

// 安裝 dd() 打印樣式組件
composer require symfony/var-dumper

微信小程序【第三方平臺賬號登錄流程圖】

微信授權登錄流程圖.png

圖片來源PHP中文網(wǎng):圖片中的3rd_session類似于Auth2的認證_token,所以后端需要寫有狀態(tài)的接口時每次驗證這個值(是否過期朽合,是否正確)自己封裝一個繼承類去驗證俱两,請看下面代碼。

  • 小程序繼承類代碼:
<?php

namespace App\Http\Controllers\Applets;

use App\Http\Controllers\Controller;
use App\Traits\AppletsTrait;

/**
 * 小程序獲取token基類認證
 *
 * Class BasisController
 * @package App\Http\Controllers\Applets
 * @author SuperHao - 619596123@qq.com
 */
class BasisController extends Controller
{
    use AppletsTrait;

    protected $session3rd;

    /**
     * BasisController constructor.
     * @author SuperHao - 619596123@qq.com
     */
    public function __construct()
    {
        $this->getSession3rd($session3rd);
        $this->session3rd = $session3rd;
    }
}
  • 小程序 TraitgetSession3rd 方法:
/**
 * 獲取session3rd參數(shù)
 *
 * @return boolean
 * @author SuperHao - 619596123@qq.com
 */
 public function getSession3rd(&$session3rd)
 {
     $result = array_key_exists('HTTP_SESSION3RD', $_SERVER);
     if (!$result) return false;
     $session3rd = $_SERVER['HTTP_SESSION3RD'];
     return true;
 }
  • 繼承基類控制器里使用:
<?php

namespace App\Http\Controllers\Applets;

use Illuminate\Http\Request;

class TestController extends BasisController
{
    /**
     * Display a listing of the resource
     *
     * @throws \Exception
     * @author SuperHao - 619596123@qq.com
     */
    public function index()
    {
        // 獲取 Session3rd (這里直接使用基類的參數(shù))
        $session3rd = $this->session3rd;

        // 獲取 redis 里的用戶信息
        $userInfo = cache($session3rd);
        
        // 數(shù)據(jù)打印
        // array:3 [
        //   "session_key" => "2C7rPDnhXAy5dUAdpS==..."
        //   "openid" => "odLil5N4yHjt2nUZfnjuIGmca..."
        //   "unionid" => "oKMyGwZjvi0WigertI0XWOP2..."
        // ]
        dd($userInfo);
    }
}
  • 中間件攔截基類Controller代碼:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Cache;

class AppletsTokenMiddleware
{
    /**
     * 獲取請求頭Session3rd.
     *
     * @param $request
     * @param Closure $next
     * @return \Illuminate\Http\JsonResponse|mixed
     * @author SuperHao - 619596123@qq.com
     */
    public function handle($request, Closure $next)
    {
        $result = array_key_exists('HTTP_SESSION3RD', $_SERVER);;
        if ($result) {
            if (Cache::has($_SERVER['HTTP_SESSION3RD'])) return $next($request);
        }
        return response()->json(['status' => false, 'code' => 401, 'message' => '賬戶過期曹步,請重新授權登陸']);
    }
}
小程序后端登錄憑證校驗:

小程序接口官網(wǎng)地址宪彩,找到登陸憑證,里面的wx.login是前端的讲婚,后臺不用管尿孔。(前提是必須了解上面的流程圖)
然后找到 登錄憑證校驗 這個接口上面寫的很詳細,好像沒有說是異步還是同步,但是網(wǎng)頁打開調用了一下你會發(fā)現(xiàn)這是個同步的接口(同步的接口就是發(fā)送參數(shù)后微信立馬回調給你參數(shù)活合,異步的話則需要配置回調域名并且還得打印寫入到日志或者文件中才能調試參數(shù))下面貼一下代碼demo:

  • 微信接口工具類
<?php

namespace App\Tools;

/**
 * 微信接口工具類
 *
 * Class WeChatTool
 * @package App\Tools
 * @author SuperHao - 619596123@qq.com
 */
class WeChatTool
{
    const OK = 0;                       // success
    const IllegalAesKey = -41001;       // encodingAesKey 非法
    const IllegalIv = -41002;           // appId missing (缺少appId參數(shù))
    const IllegalBuffer = -41003;       // aes 解密失敗
    const DecodeBase64Error = -41004;   // 解密后得到的buffer非法

    /**
     * 返回轉譯 constCodeMessage
     *
     * @param $code
     * @return string
     * @author SuperHao - 619596123@qq.com
     */
    static public function constCodeMsg($code)
    {
        switch ($code) {
            case 0:
                return 'success';
                break;
            case -41001:
                return 'encodingAesKey 非法';
                break;
            case -41002:
                return 'appId missing (缺少appId參數(shù))';
                break;
            case -41003:
                return 'aes 解密失敗';
                break;
            case -41004:
                return '解密后得到的buffer非法';
                break;
            default:
                return '未知解密錯誤';
                break;
        }
    }

    /**
     * 獲取168位的隨機字符串
     *
     * @return string
     * @author SuperHao - 619596123@qq.com
     */
    static public function getSession3rd()
    {
        /**
         * 微信的官方文檔推薦用這種操作系統(tǒng)提供真正隨機數(shù)的方法
         * /dev/urandom 和 /dev/random 已經(jīng)算是很接近真隨機數(shù)的隨機數(shù)了
         */
        return shell_exec('head -n 80 /dev/urandom | tr -dc A-Za-z0-9 | head -c 168');
    }

    /**
     * 微信接口報錯code
     *
     * @param $code
     * @return array|\Illuminate\Contracts\Translation\Translator|null|string
     * @author SuperHao - 619596123@qq.com
     */
    static public function codeErrorMessage($code)
    {
        /**
         * 判斷是否是類型中的
         */
        if (array_key_exists($code, trans('applets'))) {
            $message = $code . ': ' . trans('applets.' . $code);
        } else {
            // 未知錯誤
            $message = $code . ': ' . trans('applets.unknown');
        }

        return $message;
    }

    /**
     * 解密微信數(shù)據(jù). (檢驗數(shù)據(jù)的真實性雏婶,并且獲取解密后的明文)
     *
     * @param $sessionKey string session_key
     * @param $encryptedData string 加密的用戶數(shù)據(jù)
     * @param $iv string 與用戶數(shù)據(jù)一同返回的初始向量
     * @param $data array 解密后的原文
     * @return array
     * @author SuperHao - 619596123@qq.com
     */
    static public function decryptData($sessionKey, $encryptedData, $iv, &$data)
    {
        if (strlen($sessionKey) != 24) {
            return ['status' => false, 'code' => self::IllegalAesKey, 'message' => self::constCodeMsg(self::IllegalAesKey)];
        }
        $aesKey = base64_decode($sessionKey);
        if (strlen($iv) != 24) {
            return ['status' => false, 'code' => self::IllegalIv, 'message' => self::constCodeMsg(self::IllegalIv)];
        }

        $aesIV = base64_decode($iv);
        $aesCipher = base64_decode($encryptedData);
        $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
        $dataObj = json_decode($result);

        if ($dataObj == NULL) {
            return ['status' => false, 'code' => self::IllegalBuffer, 'message' => self::constCodeMsg(self::IllegalBuffer)];
        }
        if ($dataObj->watermark->appid != config('applets.appId')) {
            return ['status' => false, 'code' => self::IllegalBuffer, 'message' => self::constCodeMsg(self::IllegalBuffer)];
        }

        $data = $result;

        return ['status' => true, 'code' => self::OK, 'message' => self::constCodeMsg(self::OK)];
    }
}
  • 微信登錄獲取Code處理代碼
// 前端傳輸code換取sessionkey (get code2Session).
public function index(Request $request)
{
    /**
     * 驗證登陸信息
     */
    $validator = Validator::make($request->all(), [
        'js_code' => 'required|alpha_dash',
    ]);
    if ($validator->fails()) {
        return $this->response->error($validator->errors()->first());
    }

    $jsCode = $request->input('js_code');

    /**
     * getJsCode
     */
    $url = sprintf(config('applets.url.getJsCode'), config('applets.appId'), config('applets.appSecret'), $jsCode);
    /**
     * send Http
     */
    $result = CurlTool::httpsRequest($url);
    $result = json_decode($result, true);
    /**
     * Exists WeChat Error
     */
    if (array_key_exists('errcode', $result)) {
        /**
         * 微信接口報錯代碼轉譯
         */
        $message = WeChatTool::codeErrorMessage($result['errcode']);
        return $this->response->error($message);
    }

    return $this->response->success($result);
}
  • 微信登錄解密數(shù)據(jù)代碼
public function store(LoginStoreRequest $request)
{
    $json = $request->input('json');
    $sessionKey = $request->input('session_key');

    /**
     * 解密數(shù)據(jù)
     */
    $result = WeChatTool::decryptData($sessionKey, $json['encryptedData'], $json['iv'], $data);
    if (!$result['status']) {
        $data = [
            'session_key' => $sessionKey,
            'json' => $json,
        ];
        filePutData('storage/wechat.log', $data);
        $message = $result['code'] . ':' . $result['message'];
        return $this->response->error($message);
    }

    /**
     * 檢測過濾微信數(shù)據(jù)是否存在
     */
    $data = json_decode($data, true);
    if (!array_key_exists('openId', $data) && !array_key_exists('unionId', $data)) {
        return $this->response->error(trans('request.arrayKeyExists'));
    }

    /**
     * build
     */
    $build = [
        'nickname' => $data['nickName'],
        'gender' => $data['gender'],
        'openid' => $data['openId'],
        'unionid' => $data['unionId'],
        'avatarUrl' => $data['avatarUrl'],
        'country' => $data['country'],
        'province' => $data['province'],
        'city' => $data['city'],
        'language' => $data['language'],
    ];

    /**
     * 判斷是否創(chuàng)建授權過
     */
    $weChatUser = $this->wechatUser->where(['unionid' => $data['unionId']])->first();
    if (!empty($weChatUser)) {
        /**
         * save userInfo
         */
        $weChatUser->save($build);
        $data['wechat_id'] = $weChatUser->id;
    } else {
        /**
         * create weChatUser Data
         */
        $create = $this->wechatUser->create($build);
        $data['wechat_id'] = $create->id;
    }

    /**
     * 獲取 3rd_session (168位的隨機字符串)
     */
    $response = [
        'unionid' => $data['unionId'],
        'Authorization' => 'Bearer ' . WeChatTool::getSession3rd(), // session3rd
    ];
    // 編寫redis值
    $session3rdRedis = session3rd($response['Authorization']);

    /**
     * Redis 儲存微信用戶信息
     */
    Cache::remember($session3rdRedis, config('applets.cacheSession3rd'), function () use ($data) {
        return [
            'wechat_id' => $data['wechat_id'],
            'unionId' => $data['unionId'],
            'openId' => $data['openId'],
            'nickName' => $data['nickName'],
            'avatarUrl' => $data['avatarUrl'],
            'gender' => $data['gender'],
        ];
    });

    return $this->response->created(trans('response.decryptData'), $response);
}
小程序調試必備:
  • Sunny-Ngrok

Sunny-Ngrok內網(wǎng)穿透工具,是一個很簡單還不收費的軟件白指。里面有體驗隧道留晚,用這個足夠了。具體使用教程官方有詳細的視頻介紹

  • Wampserver (或者可以使用 Vagrant 自帶的網(wǎng)絡配置 public_network)

為什么推薦這個告嘲?因為每次都得開本地Vagrant虛擬機才能訪問倔丈,如果寫前后臺分離為了和前端人員調試方便,開啟Wampserver直接指向自己的Vagrant目錄訪問自己的Api會更方便別人状蜗,即便是平時自己的Vagrant不啟動前端也可以訪問接口需五。

1.配置局域網(wǎng)訪問

Win +R 打開 Cmd 輸入 ipconfig 查看自己的以太網(wǎng)局域網(wǎng)IPv4
ipconfig.png
打開 httpd-vhosts 找到 Require local 替換成 Require all granted
如下:
<VirtualHost *:80>
    ServerName 192.168.10.72
    DocumentRoot D:/Vagrant/
    <Directory  "D:/Vagrant/">
        Options +Indexes +FollowSymLinks +MultiViews
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
# 借鑒地址:
// https://jingyan.baidu.com/article/acf728fd556c93f8e410a344.html

注意:記得去自己的網(wǎng)絡里查看是否是固定的 以太網(wǎng) IPv4,如果不是請如下設置:

IP地址:192.168.10.110
子網(wǎng)掩碼:255.255.255.0
默認網(wǎng)關:192.168.10.1  //里面的10 是自己公司的網(wǎng)段

DNS:
首選DNS服務器:114.114.114.114
備用DNS服務器:8.8.8.8

2.配置 Wampserver 的 Redis

雖然你的vagrant裝了redis轧坎,但是Wampserver 是沒有的宏邮,所以需要裝一下php擴展,找了半天找到靠譜的:CSDN-Wampserver安裝redis擴展缸血,我就不搬過來了蜜氨。
但是總不能一直開著cmd運行窗,太累太low -> Windows下Redis一直后臺運行

- 到Redis目錄下 安裝redis服務:
redis-server --service-install redis.windows.conf --loglevel verbose
# 提示(Redis已成功安裝為服務) :
[13808] 20 Sep 15:04:30.063 # Granting read/write access to 'NT AUTHORITY\NetworkService' on: "D:\Redis" "D:\Redis\"
[13808] 20 Sep 15:04:30.064 # Redis successfully installed as a service.
- 啟動Redis:redis-server --service-start  關閉 -stop

一個很好用的 Laravel sql 記錄日志捎泻,它的功能大體有:
1. 記錄框架中的所有執(zhí)行過的SQL
2. 展示每條執(zhí)行速度飒炎,用來分析SQL的問題,默認情況下以毫秒為單位
3. 可以日志記錄慢查詢SQL (也以毫秒為單位笆豁,可設置慢查詢時間)

composer require mnabialek/laravel-sql-logger --dev
  • 執(zhí)行擴展包資源選擇對應數(shù)字服務類
php artisan vendor:publish
  • 組件配置文件路徑在:config/sql_logger.php
Laravel-模型關聯(lián)-遠程一對多(特殊需求)

有個需求郎汪,需要遠程查表。用的模型關聯(lián)所以嘗試了期待已久的 遠程一對多闯狱,問題就是參數(shù)有點亂煞赢,官方也沒有詳細的講解。所以親自試了好久哄孤,總結寫出以下注釋:

情況說明:(現(xiàn)在有3張表)
1是主表(文章詳情表)     2是中間表(評論表)    3是遠程表(評論人的user表)
id,comment_id            id,user_id            id,name
需求:1想拿到評論他文章的用戶名稱照筑,也就是3表的username
/**
 * 文章查詢評論人信息
 * 遠程一對多
 */
 public function commentUser()
 {
     return $this->hasManyThrough(
      1   'App\Applets\user',     // 遠程表
      2   'App\Applets\comment',  // 中間表
      3   'id',                   // 中間表對主表的關聯(lián)字段(2.id)
      4   'id',                   // 遠程表對中間表的關聯(lián)字段(3.id)
      5   'comment_id',           // 主表對中間表的關聯(lián)字段(1.comment_id)
      6   'user_id'               // 中間表對遠程表的關聯(lián)字段(2.user_id)
     );
 }

* 如果把參數(shù)分為 1、2瘦陈、3凝危、4、5晨逝、6 就是:
* 1 和 2 對應
* 3 和 5 對應
* 4 和 6 對應

借著上面的分析順道說一下別的模型關聯(lián)參數(shù):
(參數(shù)都是數(shù)字代表蛾默,有時候也可不填參數(shù),Laravel 會根據(jù)規(guī)則自己找關聯(lián))
【hasOne】    一對一            1關聯(lián)模型  2關聯(lián)表的關聯(lián)字段     3自己主表的id
【hasMany】   一對多            1關聯(lián)模型  2關聯(lián)表的關聯(lián)字段     3自己主表的id
【belongsTo】 反向一對一(多)   1關聯(lián)表    2主表關聯(lián)的關聯(lián)表ID   3關聯(lián)表ID

如果有什么不太清楚咏花,或者我寫的不好的趴生。請留言評論,隨時都回復哈昏翰,謝謝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末苍匆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子棚菊,更是在濱河造成了極大的恐慌浸踩,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件统求,死亡現(xiàn)場離奇詭異检碗,居然都是意外死亡,警方通過查閱死者的電腦和手機码邻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門折剃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人像屋,你說我怎么就攤上這事怕犁。” “怎么了己莺?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵奏甫,是天一觀的道長。 經(jīng)常有香客問我凌受,道長阵子,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任胜蛉,我火速辦了婚禮挠进,結果婚禮上,老公的妹妹穿的比我還像新娘誊册。我一直安慰自己奈梳,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布解虱。 她就那樣靜靜地躺著攘须,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殴泰。 梳的紋絲不亂的頭發(fā)上于宙,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音悍汛,去河邊找鬼捞魁。 笑死,一個胖子當著我的面吹牛离咐,可吹牛的內容都是我干的谱俭。 我是一名探鬼主播奉件,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昆著!你這毒婦竟也來了县貌?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凑懂,失蹤者是張志新(化名)和其女友劉穎煤痕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體接谨,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡摆碉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脓豪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巷帝。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扫夜,靈堂內的尸體忽然破棺而出锅睛,到底是詐尸還是另有隱情,我是刑警寧澤历谍,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布现拒,位于F島的核電站,受9級特大地震影響望侈,放射性物質發(fā)生泄漏印蔬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一脱衙、第九天 我趴在偏房一處隱蔽的房頂上張望侥猬。 院中可真熱鬧,春花似錦捐韩、人聲如沸退唠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞧预。三九已至,卻和暖如春仅政,著一層夾襖步出監(jiān)牢的瞬間垢油,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工圆丹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滩愁,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓辫封,卻偏偏與公主長得像硝枉,于是被迫代替她去往敵國和親廉丽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359