微信開發(fā)之后臺(tái)開發(fā)

上一篇主要介紹了微信的網(wǎng)頁開發(fā)商乎。這篇來介紹一下微信后臺(tái)開發(fā)。對(duì)于一般的微信運(yùn)營者箫踩,微信公眾平臺(tái)提供了配置自動(dòng)回復(fù)和自定義菜單的功能塑陵。但是對(duì)于開發(fā)人員來說感憾,很多時(shí)候這些基礎(chǔ)的功能并不能滿足我們的需求,比如我們?nèi)绻胍鶕?jù)用戶的不同屬性在用戶關(guān)注的時(shí)候推送不同的圖文令花,基礎(chǔ)的自動(dòng)回復(fù)功能就無法滿足阻桅,這個(gè)時(shí)候就需要引入微信后臺(tái)開發(fā)。

接入微信后臺(tái)開發(fā)的步驟如下:

  1. 在公眾平臺(tái)的開發(fā)-基本配置里填寫服務(wù)器配置兼都。


    微信服務(wù)器配置
  • URL表示服務(wù)器的回調(diào)地址嫂沉,微信驗(yàn)證服務(wù)器有效性的請(qǐng)求及后續(xù)事件回調(diào)都會(huì)發(fā)到這個(gè)URL上。
  • Token表示服務(wù)器秘鑰扮碧,用來生成簽名趟章,保存在服務(wù)器端。
  • EncodingAESKey表示隨機(jī)碼慎王,隨機(jī)生成即可蚓土。
  • 消息加解密方式根據(jù)業(yè)務(wù)需要選擇,一般使用明文模式即可赖淤。
  1. 驗(yàn)證服務(wù)器地址有效性蜀漆,微信會(huì)發(fā)送一個(gè)帶參數(shù)的GET請(qǐng)求到步驟1填寫的URL上,以此來驗(yàn)證服務(wù)器的有效性漫蛔。這個(gè)驗(yàn)證只有當(dāng)提交服務(wù)器配置的時(shí)候才會(huì)觸發(fā)嗜愈,所以要先把應(yīng)用部署起來再去提交步驟1的服務(wù)器配置。微信驗(yàn)證服務(wù)器地址時(shí)帶的參數(shù)如下:
  • signature微信加密簽名莽龟,signature結(jié)合了開發(fā)者填寫的token參數(shù)和請(qǐng)求中的timestamp參數(shù)、nonce參數(shù)锨天。
  • timestamp時(shí)間戳毯盈。
  • nonce隨機(jī)數(shù)。
  • echostr隨機(jī)字符串病袄。
    開發(fā)者需要對(duì)請(qǐng)求進(jìn)行校驗(yàn)搂赋,來確認(rèn)請(qǐng)求是否來自微信服務(wù)器,如果確認(rèn)成功益缠,則將echostr返回脑奠,服務(wù)器配置既提交成功频蛔。校驗(yàn)的方式如下:
    1). 將token檐束、timestamp、nonce三個(gè)參數(shù)進(jìn)行字典序排序生成字符串str1雪营。
    2). 將str1進(jìn)行sha1加密產(chǎn)生str2。
    3). 比對(duì)str2和signature齿诞,如果一致酸休,則可卻認(rèn)為請(qǐng)求來自微信服務(wù)器,返回echostr即可祷杈。
/**
 * 微信簽名驗(yàn)證
 *
 * @param signature
 * @param timestamp
 * @param nonce
 * @param echostr
 * @return
 */
public static String checkSign(String signature, String timestamp, String nonce, String echostr) {
    ApiLogger.weChatLogger.info("微信進(jìn)行接口驗(yàn)證" + StringUtils.join(signature + "; ", timestamp + "; ", nonce + "; ",
            echostr + "; "));
    String[] strings = {timestamp, nonce, SIGN_TOKEN};
    //將timestamp,nonce及token進(jìn)行字典排序
    List<String> stringList = Arrays.asList(strings);
    Collections.sort(stringList);
    String signStr = String.join("", stringList);
    //將排序后的串進(jìn)行簽名
    String signVal = SignUtil.encode(signStr, SignTypeEnum.SHA1);
    if (StringUtils.equalsIgnoreCase(signVal, signature)) {
        ApiLogger.weChatLogger.info("微信驗(yàn)證通過" + echostr);
        return echostr;
    } else {
        ApiLogger.weChatLogger.warn("微信驗(yàn)證失敗, 請(qǐng)求中的簽名為 " + signature + "實(shí)際簽名為: " + signVal);
        return "failed";
    }
}

服務(wù)器驗(yàn)證成功之后就可以按照具體的接口文檔來做開發(fā)了斑司。注意,一旦提交了服務(wù)器配置但汞,公眾平臺(tái)網(wǎng)頁端的自動(dòng)回復(fù)功能和自定義菜單功能就不可用了宿刮。

微信后臺(tái)開發(fā)又主要分為兩部分,被動(dòng)處理微信事件以及主動(dòng)調(diào)用微信接口私蕾。

被動(dòng)處理微信事件

微信對(duì)于公眾號(hào)主動(dòng)觸達(dá)用戶管理的非常嚴(yán)格糙置,除了每個(gè)月限量的群發(fā)機(jī)會(huì)以外,幾乎沒有別的手段可以主動(dòng)發(fā)送消息給用戶(只有格式內(nèi)容要求非常嚴(yán)格的模版消息可以做到)是目。但是微信會(huì)將用戶觸發(fā)的事件以POST的形式發(fā)送到公眾號(hào)配置的服務(wù)器url上谤饭,事件主要分為消息事件交互事件卡券事件懊纳。開發(fā)者在接受到這些事件后揉抵,返回響應(yīng)的xml格式,即時(shí)地回復(fù)消息給用戶嗤疯,也可以同時(shí)做一些業(yè)務(wù)邏輯冤今,比如用戶關(guān)注信息入庫之類,不過建議使用異步的方式來處理業(yè)務(wù)邏輯茂缚,避免影響消息的推送戏罢,因?yàn)槿绻⑿欧?wù)器在五秒內(nèi)收不到響應(yīng)會(huì)斷掉連接,并且重新發(fā)起請(qǐng)求脚囊,總共嘗試三次龟糕。這里還有一種介于被動(dòng)和主動(dòng)間的方式,就是服務(wù)器在收到某些事件后悔耘,在一定時(shí)間內(nèi)(48小時(shí))主動(dòng)調(diào)用客服消息接口給用戶發(fā)送客服消息讲岁。如果發(fā)送給用戶的消息依賴于業(yè)務(wù)邏輯,那么建議使用客服消息衬以。

微信事件回調(diào)都是以xml的格式POST到回調(diào)地址的缓艳,所以首先需要解析xml數(shù)據(jù)。這里推薦dom4j看峻,可以很容易地將xml解析為map對(duì)象:

@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(String xml) throws DocumentException {
    Element ele = DocumentHelper.parseText(xml).getRootElement();
    Map<String, String> result = new HashMap<>();
    //遍歷所有節(jié)點(diǎn)并存入map
    ele.elements().forEach(e -> result.put(((Element) e).getName(), ((Element) e).getText()));
    return result;
}

然后根據(jù)xml中的MsgType屬性來判斷事件類型阶淘,可能出現(xiàn)的值有:event, text, image, voice, video, shortvideo, location, link, news等,上面說到的交互事件和卡券事件都是event類型互妓,而其它的值都是消息事件溪窒,比如text就是用戶發(fā)送文本消息給公眾號(hào)的消息事件坤塞。

接受到事件后,需要做的就根據(jù)需要的消息類型組裝響應(yīng)的xml數(shù)據(jù)霉猛,并且返回給微信服務(wù)器尺锚。目前可以回復(fù)給用戶的信息有文本消息,圖片消息惜浅,語音消息瘫辩,視頻消息,音樂消息和圖文消息坛悉。比如我們現(xiàn)在需要在用戶關(guān)注的時(shí)候推送一個(gè)圖文信息給用戶伐厌,那么我們就需要組裝一個(gè)圖文消息的xml返回給微信服務(wù)器。這里有一點(diǎn)裸影,微信服務(wù)器發(fā)送給我們的xml和我們返回給微信服務(wù)器的xml都有四個(gè)公共屬性挣轨,ToUserName,F(xiàn)romUserName轩猩,CreateTime和MsgType卷扮,所以可以創(chuàng)建一個(gè)抽象基類,然后所有的微信消息都由該基類導(dǎo)出均践。我們?cè)诮M建返回對(duì)象xml的時(shí)候晤锹,只需要把接受到的xml中的ToUserName設(shè)置為返回對(duì)象的FromUserName,F(xiàn)romUserName設(shè)置為ToUserName彤委,其它屬性按照官方文檔設(shè)置即可鞭铆。

將對(duì)象轉(zhuǎn)化為xml可以使用XStream,以下例子是將微信圖文對(duì)象轉(zhuǎn)化為xml的代碼焦影,WeChatNews和WeChatArticle這兩個(gè)類都是自定義的類车遂,用來表示微信圖文和微信圖文里的文章,List<WeChatArticle>是WeChatNews的一個(gè)成員屬性:

public static XStream xStream = new XStream();

/**
 * 圖文消息對(duì)象轉(zhuǎn)換成xml
 *
 * @param news 圖文消息對(duì)象
 * @return xml
 */
public static String newsMessageToXml(WeChatNews news) {
    xStream.alias("xml", news.getClass());
    xStream.alias("item", new WeChatArticle().getClass());
    return xStream.toXML(news);
}

由于xml內(nèi)容中可能出現(xiàn)<或者&等符號(hào)斯辰,導(dǎo)致xml解析器解析錯(cuò)誤舶担,所以最好將返回對(duì)象xml的值都放在CDATA中〗费模可以通過對(duì)XStream進(jìn)行擴(kuò)展:

private static final CDATA_START = "<![CDATA[";
private static final CDATA_END = "]]>";

/**
 * 擴(kuò)展xstream柄沮,使其支持CDATA塊
 */
public static XStream xstream = new XStream(new XppDriver() {
    public HierarchicalStreamWriter createWriter(Writer out) {
        return new PrettyPrintWriter(out) {
            protected void writeText(QuickWriter writer, String text) {
                writer.write(CDATA_START);
                writer.write(text);
                writer.write(CDATA_END);
            }
        };
    }
});

P.S. 這里再來吐槽一下微信的官方文檔。我在開發(fā)的時(shí)候遇到過幾處文檔有誤或者文檔缺失的問題:

比如當(dāng)用戶通過微信支付成功后的關(guān)注公眾號(hào)選項(xiàng)關(guān)注時(shí)废岂,微信會(huì)發(fā)送一個(gè)subscribe事件到回調(diào)地址,并且?guī)в衑ventKey(格式為last_trade_no_xxxxxxxxxxx)狱意,而文檔中根本沒有這個(gè)類型的事件的描述湖苞。文檔中只有關(guān)注事件和通過推廣二維碼關(guān)注的事件是subscribe事件,但是前者沒有eventKey详囤,后者eventKey的格式為qrscene_xxxxxx财骨。文檔明顯漏了這一關(guān)注類型镐作。

又比如微信偶爾會(huì)發(fā)一個(gè)不帶參數(shù)的GET請(qǐng)求到回調(diào)地址,這個(gè)就很莫名其妙了隆箩,我翻遍了文檔沒有發(fā)現(xiàn)相關(guān)說明该贾。這種請(qǐng)求只能在代碼中屏蔽掉,問了微信的技術(shù)說是內(nèi)部bug捌臊,不知何時(shí)能解決杨蛋。搜了搜網(wǎng)上也有遇到這個(gè)問題的。

主動(dòng)調(diào)用微信接口

微信后臺(tái)開發(fā)另外一塊很大的內(nèi)容就是主動(dòng)調(diào)用微信接口來實(shí)現(xiàn)一些業(yè)務(wù)邏輯理澎。調(diào)用微信接口首先需要微信基礎(chǔ)服務(wù)access_token逞力。這個(gè)access_token和上一篇講的網(wǎng)頁授權(quán)access_token不一樣,這個(gè)token是調(diào)用微信接口的令牌糠爬。該令牌需要通過請(qǐng)求微信接口獲得:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
注意寇荧,調(diào)用前需要先在微信開發(fā)-基本配置里配置IP白名單,這樣才能調(diào)用獲取access_token接口执隧。
正常返回如下:

{"access_token":"ACCESS_TOKEN","expires_in":7200}

由于獲取令牌的接口每天的調(diào)用次數(shù)有限(100000次)揩抡,所以需要將獲取到access_token保存起來,并且由于令牌兩小時(shí)會(huì)過期镀琉,需要提供主動(dòng)/被動(dòng)的刷新機(jī)制峦嗤。由于線上一般都是分布式的應(yīng)用部署方式,所以為了避免多臺(tái)服務(wù)器同時(shí)去更新access_token滚粟,需要使用分布式鎖寻仗。分布式鎖的實(shí)現(xiàn)方式一般有利用數(shù)據(jù)庫樂觀鎖的,利用redis/memcache的原子性操作的凡壤,以及利用zookeeper的最小節(jié)點(diǎn)的署尤。這里講一下我在代碼中使用的redis的實(shí)現(xiàn)分布式鎖的大致步驟。

  1. 使用setnx("MyKey", 當(dāng)前時(shí)間+過期超時(shí)時(shí)間) 亚侠,如果返回1曹体,則獲取鎖成功,那么就可以去更新access_token硝烂,并且返回最新的access_token箕别;如果返回0則沒有獲取到鎖,轉(zhuǎn)向2滞谢。
  2. get("MyKey")獲取值老鎖過期時(shí)間oldExpireTime 串稀,并將這個(gè)值與當(dāng)前的系統(tǒng)時(shí)間進(jìn)行比較,如果大于當(dāng)前系統(tǒng)時(shí)間狮杨,說明鎖還未過期母截,別的請(qǐng)求可能正在更新access_token,直接返回空橄教;如果小于當(dāng)前系統(tǒng)時(shí)間清寇,則認(rèn)為這個(gè)鎖已經(jīng)過期喘漏,那么可以允許別的請(qǐng)求重新獲取,轉(zhuǎn)向3华烟。
  3. 計(jì)算新鎖過期時(shí)間newExpireTime=當(dāng)前時(shí)間+鎖過期時(shí)間(一個(gè)常量)翩迈,然后使用getset("MyKey", newExpireTime),設(shè)置鎖的新過期時(shí)間為newExpireTime盔夜,這個(gè)操作同時(shí)會(huì)返回當(dāng)前MyKey的值currentExpireTime负饲。
  4. 判斷鎖當(dāng)前過期時(shí)間currentExpireTime與老鎖過期時(shí)間oldExpireTime是否相等,如果相等比吭,說明獲取鎖成功绽族,那么可以接著更新access_token,并且返回access_token衩藤。如果不相等吧慢,說明這個(gè)鎖又被別的請(qǐng)求獲取走了,因?yàn)樵谶@個(gè)getset操作前已經(jīng)有別的請(qǐng)求執(zhí)行了getset赏表,所以currentExpireTime發(fā)生了變化检诗。那么當(dāng)前請(qǐng)求可以直接返回空。
  5. 在更新access_token后瓢剿,檢查分布式鎖是否過期逢慌,如果過期則使用delete釋放鎖,否則保留间狂;這里注意一點(diǎn)攻泼,不要在更新access_token后直接刪除鎖,否則會(huì)在高并發(fā)時(shí)鉴象,出現(xiàn)在第2步進(jìn)程獲取老鎖時(shí)為空或者第3步getset獲取當(dāng)前鎖過期時(shí)間為空的情況忙菠。
基于redis實(shí)現(xiàn)的分布式鎖.png

調(diào)用上述獲取access_token的服務(wù)應(yīng)嘗試三次,直到返回access_token為止纺弊。如果返回為空牛欢,則等待一秒后再獲取,如果三次后如果還未獲取到access_token淆游,則拋出異常傍睹。由于大多數(shù)情況,有效的access_token應(yīng)該在緩存中可以直接取到犹菱,不需要通過分布式鎖更新拾稳,所以不要在同步整個(gè)獲取access_token的服務(wù),負(fù)責(zé)可能會(huì)導(dǎo)致高并發(fā)下的阻塞導(dǎo)致性能瓶頸腊脱。更不能只將更新access_token的邏輯同步熊赖,這樣會(huì)導(dǎo)致重復(fù)更新access_token,而失去了分布式鎖的意義虑椎。

拿到access_token后就可以根據(jù)微信文檔來調(diào)用響應(yīng)的接口了震鹉。這里拿獲取用戶信息接口來舉例。該接口請(qǐng)求說明如下:
http請(qǐng)求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
帶上access_token以及用戶的openid捆姜,就可以向微信請(qǐng)求用戶信息了传趾。HttpClient建議使用RestTemplate,方便簡(jiǎn)潔泥技。

這里又有一個(gè)微信文檔的坑浆兰,獲取用戶信息接口實(shí)際返回的數(shù)據(jù)比文檔中要多一些屬性(應(yīng)該是2018/3/6晚上新增了subscribe_scene, qr_scene和qr_scene_str字段,文檔并沒有及時(shí)更新珊豹。)所以這里需要在轉(zhuǎn)化json到對(duì)象時(shí)簸呈,忽略對(duì)象中沒有的屬性,可以通過配置ObjectMapper對(duì)象來實(shí)現(xiàn):

public class CustomObjectMapper {

public final static ObjectMapper om = new ObjectMapper();

static {
    om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }
}

調(diào)用微信接口的代碼:

//微信API接口域名
private static final String API_HOST = "https://api.weixin.qq.com/cgi-bin/";
//10秒鏈接超時(shí)
private static final int CONNECT_TIMEOUT = 10 * 1000;
//1分鐘接收數(shù)據(jù)接收時(shí)間
private static final int READ_TIMEOUT = 60 * 1000;

private RestTemplate restTemplate;

public WeChatServiceImpl() {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setConnectTimeout(CONNECT_TIMEOUT);
    requestFactory.setReadTimeout(READ_TIMEOUT);
    this.restTemplate = new RestTemplate(requestFactory);
    this.restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}

/**
 * 通過微信api獲取微信用戶信息
 *
 * @param openId
 * @return
 */
private WeChatUser getWeChatUser(String openId) {
    String url = String.format("%suser/info?access_token=%s&openid=%s&lang=zh_CN", API_HOST, weChatTokenService
            .getToken().getAccessToken(), openId);
    ApiLogger.weChatLogger.info("調(diào)用微信接口獲取微信用戶信息:" + url);

    try {
        String result = restTemplate.getForObject(url, String.class);
        ApiLogger.weChatLogger.debug("調(diào)用微信接口獲取微信用戶信息:" + result);
        return CustomObjectMapper.om.readValue(result, WeChatUser.class);
    } catch (Exception e) {
        String msg = "調(diào)用微信接口獲取微信用戶信息失數瓴琛:" + url;
        ApiLogger.weChatLogger.error(msg, e);
        throw new WeChatEx(msg, e);
    }
}

同時(shí)蜕便,也可以直接到微信公眾平臺(tái)的開發(fā)者工具中,使用接口調(diào)試工具直接調(diào)試和請(qǐng)求接口贩幻。比如生成自定義菜單這種一次性的調(diào)用就非常適合在接口調(diào)試工具中直接調(diào)用轿腺。微信開放了很多有用的接口,比如生成推廣二維碼的接口丛楚,拉取關(guān)注用戶的接口族壳,發(fā)送模版消息的接口,發(fā)送客服消息的接口等等趣些,具體可參照官方文檔仿荆。

花了兩篇文章的篇幅,大概地講了一下微信的網(wǎng)頁開發(fā)和后臺(tái)開發(fā)坏平,也提到了一些我在開發(fā)中遇到的難點(diǎn)和注意點(diǎn)拢操,希望對(duì)大家有用。微信開發(fā)不僅僅包括這兩塊功茴,還有微信jssdk的使用庐冯,微信支付,微信開放平臺(tái)開發(fā)等等坎穿。大家有興趣的話也可以多看看官方文檔展父,雖然里面有不少坑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玲昧,一起剝皮案震驚了整個(gè)濱河市栖茉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孵延,老刑警劉巖吕漂,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尘应,居然都是意外死亡惶凝,警方通過查閱死者的電腦和手機(jī)吼虎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苍鲜,“玉大人思灰,你說我怎么就攤上這事』焯希” “怎么了洒疚?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坯屿。 經(jīng)常有香客問我油湖,道長(zhǎng),這世上最難降的妖魔是什么领跛? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任乏德,我火速辦了婚禮,結(jié)果婚禮上隔节,老公的妹妹穿的比我還像新娘鹅经。我一直安慰自己,他們只是感情好怎诫,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布瘾晃。 她就那樣靜靜地躺著,像睡著了一般幻妓。 火紅的嫁衣襯著肌膚如雪蹦误。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天肉津,我揣著相機(jī)與錄音强胰,去河邊找鬼。 笑死妹沙,一個(gè)胖子當(dāng)著我的面吹牛偶洋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播距糖,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼玄窝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了悍引?” 一聲冷哼從身側(cè)響起恩脂,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趣斤,沒想到半個(gè)月后俩块,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年玉凯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了势腮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壮啊,死狀恐怖嫉鲸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歹啼,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布座菠,位于F島的核電站狸眼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浴滴。R本人自食惡果不足惜拓萌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望升略。 院中可真熱鬧微王,春花似錦、人聲如沸品嚣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翰撑。三九已至罩旋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間眶诈,已是汗流浹背涨醋。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逝撬,地道東北人浴骂。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宪潮,于是被迫代替她去往敵國和親溯警。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359