微信小程序推送公眾號(hào)模版消息

微信推送公眾號(hào)模版消息通知 -- machao


業(yè)務(wù)場(chǎng)景:

某一天,小明開車出去接老婆的機(jī),路上不小心碰撞到了其他的車,于是他通過車險(xiǎn)小程序把事故現(xiàn)場(chǎng)上傳給理賠人員處理.
過了一段時(shí)間,經(jīng)過理賠人員的審核后,覺得賠付沒有問題,需要發(fā)送案件進(jìn)度通知給小明.
這時(shí)候,理賠人員問他們的小程序的開發(fā)人員,我們應(yīng)該選擇那種消息通知策略比較合理呢?


消息通知策略:

1. 小程序模版消息

    模板推送位置:服務(wù)通知

    模板下發(fā)條件:用戶本人在微信體系內(nèi)與頁(yè)面有交互行為后觸發(fā)锈拨,包括: 支付,提交表單

    模板跳轉(zhuǎn)能力:點(diǎn)擊查看詳情,僅能跳轉(zhuǎn) 下發(fā)模板的小程序的各個(gè)頁(yè)面

2. 公眾號(hào)模版消息

    模板推送位置:公眾號(hào)

    模版下發(fā)能力: 服務(wù)端可主動(dòng)下發(fā)消息

    模板跳轉(zhuǎn)能力:點(diǎn)擊查看詳情,能跳轉(zhuǎn) 下發(fā)模板消息的公眾號(hào)是綁定關(guān)聯(lián)關(guān)系的小程序的各個(gè)頁(yè)面

3. 小程序模版消息效果與公眾號(hào)模版消息效果對(duì)比:

效果對(duì)比

分析:

通過小程序模版消息推送, 消息會(huì)被推送到“服務(wù)通知”欄目中,“服務(wù)通知”欄目中會(huì)存在很多其他小程序的推送,這樣看起來會(huì)很雜亂.

另外,小程序模版消息推送前提, 必須要在用戶本人在微信體系內(nèi)與頁(yè)面有交互行為后觸發(fā), 即不能延遲推送. 當(dāng)然也可以通過保存交互過程中的fromid達(dá)到延遲推送的效果, 但是這個(gè)消息只能推送給觸發(fā)這個(gè)交互行為的用戶, 這樣會(huì)導(dǎo)致另外一個(gè)問題: 假如理賠人員除了希望推送案件進(jìn)度通知給小明外,還希望把消息推送給上級(jí)領(lǐng)導(dǎo),那這個(gè)就做不到了.

因此,推送公眾號(hào)模版消息才是最便捷的策略.


如何實(shí)現(xiàn)微信小程序推送公眾號(hào)模版消息?

首先我們需要清楚以下幾點(diǎn):
1. 消息發(fā)給誰? -- who?
2. 消息怎么發(fā)? -- how?
3. 消息內(nèi)容發(fā)什么? -- what?

對(duì)于第一點(diǎn)(who?)

由于我們的主體是小程序, 因此我們沒有辦法直接通過用戶的小程序openid直接進(jìn)行公眾號(hào)模版消息的發(fā)送, 這時(shí)候上面的unionId機(jī)制就起了至關(guān)重要的作用了.


UnionID機(jī)制

我們可以通過將小程序和公眾號(hào)掛載在同一個(gè)微信開放平臺(tái)帳號(hào)下, 通過unionId進(jìn)行邏輯關(guān)聯(lián), 這時(shí)候我們就可以通過用戶的小程序openid找到用戶的公眾號(hào)openid,進(jìn)而進(jìn)行公眾號(hào)的模版消息推送.

由于開發(fā)者經(jīng)常有需在多個(gè)平臺(tái)(移動(dòng)應(yīng)用曼月、網(wǎng)站、公眾帳號(hào))之間共通用戶帳號(hào)厦幅,統(tǒng)一帳號(hào)體系的需求野芒,微信開放平臺(tái)提供了UnionID機(jī)制。
換句話說,同一用戶档玻,對(duì)同一個(gè)微信開放平臺(tái)帳號(hào)下的不同應(yīng)用软棺,UnionID是相同的县貌。
微信開放平臺(tái)帳號(hào)下的不同應(yīng)用包括: 小程序, 公眾號(hào), 移動(dòng)應(yīng)用等.

對(duì)于第二點(diǎn)(how?)

小程序的模版消息有自己的推送模版消息的api接口, 公眾號(hào)的模版消息也有自己的推送模版消息的api接口. 為了便捷管理優(yōu)化, 于是微信api推出一個(gè)“統(tǒng)一服務(wù)消息”接口, 我們接下來也將使用這個(gè)接口進(jìn)行消息的下發(fā).


統(tǒng)一服務(wù)消息
對(duì)于第三點(diǎn)(what?)

這里當(dāng)然就是發(fā)送模版消息啦, 需要到 公眾號(hào)-模版消息 新增自己的模版.


思路:

  • 將小程序和公眾號(hào)掛載在同一個(gè)開發(fā)平臺(tái)賬號(hào)下,這樣就能多個(gè)不同的主體共用一個(gè)相同的UnionID啦.
  • 在用戶登錄小程序時(shí), 通過小程序的獲取用戶信息接口, 得到并保存用戶的小程序openid + unionId.
  • 同時(shí)引導(dǎo)用戶在小程序內(nèi)通過公眾號(hào)網(wǎng)頁(yè)授權(quán), 從而得到并保存用戶的公眾號(hào)openid + unionId.
  • 小程序用戶通過unionId關(guān)聯(lián)找到公眾號(hào)openid,進(jìn)而下發(fā)模版消息.

準(zhǔn)備:

  1. 公眾號(hào)的appid+ secret
    小程序的appid+ secret
  2. 前往微信開放平臺(tái)-管理中心,將公眾號(hào),小程序都綁定在同一個(gè)開放平臺(tái)中
  3. 認(rèn)證服務(wù)器為公眾號(hào)開發(fā)者[只需認(rèn)證一次]
    a. 進(jìn)入微信公眾平臺(tái),登錄公眾號(hào)賬號(hào)
    b. 開發(fā)-基本配置, 填寫服務(wù)器配置
    填寫服務(wù)器配置

    c. 驗(yàn)證服務(wù)器地址的有效性

公眾號(hào)開發(fā)者認(rèn)證服務(wù)器詳細(xì)步驟

認(rèn)證服務(wù)器為公眾號(hào)開發(fā)者詳細(xì)步驟:

  • 添加開發(fā)者服務(wù)器認(rèn)證接口
@Api("微信公眾號(hào)開發(fā)者API接口")
@Controller
@RequestMapping("/wxPublic/serverApi")
public class WxServerApi {
  private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
  
  //在 微信公眾號(hào)-服務(wù)器配置 中配置, 對(duì)應(yīng)“令牌(Token)”
  @Value("${wx.public.wxServerAuthenToken}")
  String wxServerAuthenToken;

  /**
   * 開發(fā)者認(rèn)證接口
   * 微信api用于認(rèn)證 服務(wù)器 是否為可用服務(wù)器
   * @param signature
   * @param timestamp
   * @param nonce
   * @param echostr
   * @return
   */
  @ApiOperation(value = "開發(fā)者認(rèn)證接口", notes = "開發(fā)者認(rèn)證接口")
  @GetMapping("/wxAuthenConfig")
  @ResponseBody
  public String wxAuthenConfig(String signature, String timestamp, String nonce, String echostr) {

      LOGGER.info("開發(fā)者認(rèn)證接口 - 開始簽名驗(yàn)證:" + " PARAM VAL: >>>" + signature + "\t" + timestamp + "\t" + nonce + "\t" + echostr);
      
      if (StringUtils.isNotEmpty(signature) && StringUtils.isNotEmpty(timestamp) && StringUtils.isNotEmpty(nonce)
              && StringUtils.isNotEmpty(echostr)) {
          String sTempStr = "";
          try {
              sTempStr = SHA1.getSHA1(timestamp, nonce, wxServerAuthenToken, "");
          } catch (Exception e) {
              e.printStackTrace();
          }

          if (StringUtils.isNotEmpty(sTempStr) && StringUtils.equals(signature, sTempStr)) {
              LOGGER.info("開發(fā)者認(rèn)證接口 - 開始簽名驗(yàn)證 - 驗(yàn)證成功:-----------:" + sTempStr);
              return echostr;
          } else {
              LOGGER.info("開發(fā)者認(rèn)證接口 - 開始簽名驗(yàn)證 - 驗(yàn)證失斄兆础:-----------:00000");
              return "-1";
          }
      } else {
          LOGGER.info("開發(fā)者認(rèn)證接口 - 開始簽名驗(yàn)證 - 驗(yàn)證失斆圊巍:-----------:11111");
          return "-1";
      }
  }

}
/**
* SHA1 class
*
* 計(jì)算公眾平臺(tái)的消息簽名接口.
*/
public class SHA1 {

  /**
   * 用SHA1算法生成安全簽名
   * @param token 票據(jù)
   * @param timestamp 時(shí)間戳
   * @param nonce 隨機(jī)字符串
   * @param encrypt 密文
   * @return 安全簽名
   * @throws AesException 
   */
  public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
            {
      try {
          String[] array = new String[] { token, timestamp, nonce, encrypt };
          StringBuffer sb = new StringBuffer();
          // 字符串排序
          Arrays.sort(array);
          for (int i = 0; i < 4; i++) {
              sb.append(array[i]);
          }
          String str = sb.toString();
          // SHA1簽名生成
          MessageDigest md = MessageDigest.getInstance("SHA-1");
          md.update(str.getBytes());
          byte[] digest = md.digest();

          StringBuffer hexstr = new StringBuffer();
          String shaHex = "";
          for (int i = 0; i < digest.length; i++) {
              shaHex = Integer.toHexString(digest[i] & 0xFF);
              if (shaHex.length() < 2) {
                  hexstr.append(0);
              }
              hexstr.append(shaHex);
          }
          return hexstr.toString();
      } catch (Exception e) {
          e.printStackTrace();
          throw new AesException(AesException.ComputeSignatureError);
      }
  }
}
  • 在開發(fā)-基本配置, 填寫服務(wù)器配置
    “服務(wù)器地址(URL)” 填寫 認(rèn)證接口的地址
    “令牌(Token)” 填寫 代碼中的wxServerAuthenToken的值
    "消息加解密方式" 選擇 明文
  • 提交修改后, 微信會(huì)請(qǐng)求訪問接口, 這樣認(rèn)證就完成了.

在用戶登錄小程序時(shí), 通過小程序的獲取用戶信息接口,獲得用戶的小程序openid + unionId

步驟
  • 小程序通過wx.login()獲得 code
wx.login({
       success(res){
         let code=res.code
       },  
       fail(res){
         that.$message("微信登錄失敗锐极,請(qǐng)退出重試笙僚!")
       }
     })
  • 服務(wù)后端通過code + 小程序appid + 小程序secret, 請(qǐng)求 auth.code2Session API接口得到用戶的小程序openid + unionId
/**
* 微信小程序openid工作類
* @author mac
*
*/
@SuppressWarnings("deprecation")
@Component
public class WxOpenidUtil {
  private Logger LOGGER = >LoggerFactory.getLogger(this.getClass());

  final String openidUrl = "https://api.weixin.qq.com/sns/jscode2session";

  @Value("${wx.applet.appid}")
  String wxAppid;
  @Value("${wx.applet.secret}")
  String wxSecret;
  @Value("${wx.applet.grantType}")
  String grantType;

  @SuppressWarnings("resource")
  public WxOpenIdPo GetWxOpenId(String wxcode) {

      WxOpenIdPo info = null;
      // 微信API接口
      String url = openidUrl + "?appid=" + wxAppid + "&secret=" + wxSecret + "&js_code=" + wxcode + "&grant_type=" + grantType + "";

      HttpGet request = new HttpGet(url);
      HttpResponse response = null;
      try {
          HttpClient client = new DefaultHttpClient();
          response = client.execute(request);
          if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              String strResult = EntityUtils.toString(response.getEntity());
              if (!StringUtils.isEmpty(strResult)) {
                  info = JacksonUtil.defaultInstance().json2pojo(strResult, WxOpenIdPo.class);
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          LOGGER.error("根據(jù)wxcode獲取微信小程序openid失敗: wxcode(" + wxcode + ")");
      }

      return info;
  }

}
  • 將獲取到的 用戶的小程序openid + unionId保存

引導(dǎo)用戶在小程序內(nèi)通過公眾號(hào)網(wǎng)頁(yè)授權(quán), 從而得到并保存用戶的公眾號(hào)openid + unionId.

步驟:
  • 服務(wù)后端添加“公眾號(hào)授權(quán)網(wǎng)頁(yè)url”接口, 返回“公眾號(hào)授權(quán)網(wǎng)頁(yè)url“
  • 服務(wù)后端添加回調(diào)接口, 用于微信api回調(diào), 接口會(huì)攜帶用戶的公眾號(hào)code(劃重點(diǎn))
  • 小程序內(nèi)通過web-view標(biāo)簽訪問“公眾號(hào)授權(quán)網(wǎng)頁(yè)url”接口返回的url, 引導(dǎo)用戶公眾號(hào)授權(quán), 授權(quán)通過后, 微信api會(huì)回調(diào)“公眾號(hào)授權(quán)網(wǎng)頁(yè)url”提供的回調(diào)接口
  • 在回調(diào)接口接口中, 通過微信api攜帶的用戶公眾號(hào)code, 獲取網(wǎng)頁(yè)授權(quán)access_token+用戶的公眾號(hào)openid
    (我們這里
    把 [網(wǎng)頁(yè)授權(quán)access_token] 叫為 [Oauth2AccessToken],
    把 [基礎(chǔ)支持中的access_token] 叫為 [PublicAccessToken] ,
    這樣大家容易理解
    )
  • 這時(shí)候,我們已經(jīng)得到用戶的公眾號(hào)openid了,但是沒有用戶的unionId,沒有辦法與用戶的小程序openid進(jìn)行邏輯關(guān)聯(lián). 于是我們還要通過 Oauth2AccessToken + openid 拉取用戶信息, 這個(gè)接口會(huì)返回用戶的unionId,這時(shí)公眾號(hào)與小程序的用戶正式邏輯關(guān)聯(lián)起來了.
貼代碼啦:
  • 1.“公眾號(hào)授權(quán)網(wǎng)頁(yè)url”接口
  @Value("${wx.public.appid}")
  String publicAppid;
  @Value("${wx.public.secret}")
  String publicSecret;
  @Value("${yd.baseHost}")
  String baseHost;//應(yīng)用鏈接
  @Value("${wx.public.clientReceiveOpenidUrl}")
  String clientReceiveOpenidUrl;//接口路徑
  @Value("${wx.public.clientState}")
  String clientState;//state, 用于回調(diào)接口檢驗(yàn)
  
  /**
   * 返回 微信公眾號(hào)獲取openid的url (并添加回調(diào)路徑)
   * @throws UnsupportedEncodingException 
   */
  @ApiOperation(value = "返回 微信公眾號(hào)獲取openid的url (并添加回調(diào)路徑)", notes = "返回 微信公眾號(hào)獲取openid的url (并添加回調(diào)路徑)")
  @PostMapping("/return2OpenidUrl")
  @ResponseBody
  public String return2OpenidUrl() throws UnsupportedEncodingException{
      LOGGER.info("微信公眾號(hào)OpenId接口 - 返回 微信公眾號(hào)獲取openid的url - 開始");
      
      StringBuffer encodeUrl = new StringBuffer(300);
      encodeUrl.append(baseHost + clientReceiveOpenidUrl);
      String redirectUrl = URLEncoder.encode(encodeUrl.toString(), "utf-8");
      
      
      StringBuffer sb = new StringBuffer();
      sb.append("https://open.weixin.qq.com/connect/oauth2/authorize?appid=");
      sb.append(publicAppid);
      sb.append("&redirect_uri=");
      sb.append(redirectUrl);
      sb.append("&response_type=code&scope=snsapi_userinfo");
      sb.append("&state=");
      sb.append(clientState);
      
      LOGGER.info("微信公眾號(hào)OpenId接口 - 返回 微信公眾號(hào)獲取openid的url - 結(jié)束 - redirectUrl("+sb.toString()+")");
      
      return sb.toString();
  }
    1. 服務(wù)后端添加回調(diào)接口
  @Autowired
  Oauth2AccessTokenBuilder oauth2AccessTokenBuilder;
  @Autowired
  SnsapiUserinfoUtil snsapiUserinfoUtil;
  @Autowired
  UserMapper userMapper;
  
  @Value("${wx.public.appid}")
  String publicAppid;
  @Value("${wx.public.secret}")
  String publicSecret;
  @Value("${yd.baseHost}")
  String baseHost;
  @Value("${wx.public.clientReceiveOpenidUrl}")
  String clientReceiveOpenidUrl;
  @Value("${wx.public.clientState}")
  String clientState;

  /**
   * 初始化 公眾號(hào)用戶基本信息(openid+unionid) 接口
   * - 微信公眾號(hào)回調(diào)的接口 [攜帶上 code=CODE&state=STATE]
   * @param request
   * @param response
   * @return
   */
  @GetMapping("/initPublicUserInfo")
  @ResponseBody
  public void initPublicUserInfo(@RequestParam("code") String code,@RequestParam("state") String state){
      LOGGER.info("微信公眾號(hào)OpenId接口 - 初始化 公眾號(hào)用戶基本信息(openid+unionid) 接口 - 開始 - code("+code+") state("+state+")");
      
      //校驗(yàn) 重定向攜帶的state參數(shù)
      if(!clientState.equals(state)){
          LOGGER.info("微信公眾號(hào)OpenId接口 - 初始化 公眾號(hào)用戶基本信息(openid+unionid) 接口 - state不匹配 - 結(jié)束");
          return;
      }
      
      
      //通過code換取網(wǎng)頁(yè)授權(quán)access_token + openid
      //這里通過code換取的是一個(gè)特殊的網(wǎng)頁(yè)授權(quán)access_token,與基礎(chǔ)支持中的access_token(該access_token用于調(diào)用其他接口)不同
      if(StringUtils.isEmpty(code)){
          LOGGER.info("微信公眾號(hào)OpenId接口 - 初始化 公眾號(hào)用戶基本信息(openid+unionid) 接口 - code為空 - 結(jié)束");
          return;
      }
      Oauth2AccessToken oauth2AccessToken = new Oauth2AccessToken(oauth2AccessTokenBuilder, code);
      String accessToken = oauth2AccessToken.getAccess_token();
      String openid = oauth2AccessToken.getOpenid();
      LOGGER.info("微信公眾號(hào)OpenId接口 - 初始化 公眾號(hào)用戶基本信息(openid+unionid) 接口 - 通過code換取網(wǎng)頁(yè)授權(quán)access_token + openid - accessToken("+accessToken+") openid("+openid+")");
      
      
      //根據(jù) access_token + openid 拉取用戶信息(需scope為 snsapi_userinfo)
      //得到 unionid + 用戶基本信息
      SnsapiUserinfo snsapiUserinfo = snsapiUserinfoUtil.getSnsapiUserinfo(accessToken, openid);
      String unionid = snsapiUserinfo.getUnionid();
      
      
      //保存 公眾號(hào)openid
      User usr = new User();
      usr.setUnionid(unionid);
      User one = userMapper.selectOne(usr);
      if(one != null){
          one.setPublicAccountOpenid(openid);
          userMapper.updateByPrimaryKey(one);
      }
      
 }

網(wǎng)頁(yè)授權(quán)access_token(Oauth2AccessToken)

/**
* 網(wǎng)頁(yè)OAuth2授權(quán) 接口返回值
* @author mac
*
*/
public class Oauth2AccessToken {
      
  //網(wǎng)頁(yè)授權(quán)接口調(diào)用憑證,注意:此access_token與基礎(chǔ)支持的access_token不同
  private String access_token;
  //用戶唯一標(biāo)識(shí),請(qǐng)注意灵再,在未關(guān)注公眾號(hào)時(shí)肋层,用戶訪問公眾號(hào)的網(wǎng)頁(yè),也會(huì)產(chǎn)生一個(gè)用戶和公眾號(hào)唯一的OpenID
  private String openid;
  
  
  public String getAccess_token() {
      return access_token;
  }
  public String getOpenid() {
      return openid;
  }
  
  /**
   * 使用構(gòu)建類 構(gòu)建
   */
  public Oauth2AccessToken(Oauth2AccessTokenBuilder builder, String code){
      Oauth2AccessTokenBuilder build = builder.build(code);
      this.access_token = build.accessToken;
      this.openid = build.openid;
  }
  
}

網(wǎng)頁(yè)OAuth2授權(quán) 構(gòu)建類(Oauth2AccessTokenBuilder)

/**
* 網(wǎng)頁(yè)OAuth2授權(quán) 構(gòu)建類
* 
* @author mac
*
*/
@SuppressWarnings("deprecation")
@Component
public class Oauth2AccessTokenBuilder {
  private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

  @Value("${wx.public.appid}")
  String publicAppid;
  @Value("${wx.public.secret}")
  String publicSecret;
  
  //網(wǎng)頁(yè)OAuth2授權(quán) url
  final private String oauth2AccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
  //刷新OAuth2授權(quán) url 
  //final private String oauth2RefreshTokenUrl = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";

  // 網(wǎng)頁(yè)授權(quán)接口調(diào)用憑證,注意:此access_token與基礎(chǔ)支持的access_token不同
  public String accessToken;
  // 用戶刷新access_token
  //public String refreshToken;
  // 用戶唯一標(biāo)識(shí)翎迁,請(qǐng)注意栋猖,在未關(guān)注公眾號(hào)時(shí),用戶訪問公眾號(hào)的網(wǎng)頁(yè)鸳兽,也會(huì)產(chǎn)生一個(gè)用戶和公眾號(hào)唯一的OpenID
  public String openid;

  /**
   * 構(gòu)建 oauth2AccessTokenBuilder
   * @param code 微信用戶code
   * @return
   */
  public Oauth2AccessTokenBuilder build(String code) {
      //不需要重復(fù)刷新網(wǎng)頁(yè)授權(quán)token
      //直接根據(jù)code請(qǐng)求得到token+openid即可
      Oauth2AccessTokenRespPo po = getAccessToken(code);
      if(po != null && po.errcode != null && !StringUtils.isEmpty(po.errmsg)){
          return null;
      }
      this.accessToken = po.access_token;
      this.openid = po.openid;
      return this;
  }

  /**
   * 獲取網(wǎng)頁(yè)授權(quán)的access_token + openID
   * 
   * @return
   */
  @SuppressWarnings({ "resource" })
  private Oauth2AccessTokenRespPo getAccessToken(String code) {
      // 獲取小程序全局唯一后臺(tái)接口調(diào)用憑據(jù) 接口
      String url = oauth2AccessTokenUrl;
      url = url.replace("APPID", publicAppid);
      url = url.replace("SECRET", publicSecret);
      url = url.replace("CODE", code);
      LOGGER.info("網(wǎng)頁(yè)OAuth2授權(quán) 構(gòu)建類 - 獲取網(wǎng)頁(yè)授權(quán)的access_token + openID - url: "+ url);

      HttpGet request = new HttpGet(url);
      HttpResponse response = null;
      Oauth2AccessTokenRespPo po = null;

      try {
          HttpClient client = new DefaultHttpClient();
          response = client.execute(request);
          if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              String strResult = EntityUtils.toString(response.getEntity());
              if (!StringUtils.isEmpty(strResult)) {
                  LOGGER.info("網(wǎng)頁(yè)OAuth2授權(quán) 構(gòu)建類 - 獲取網(wǎng)頁(yè)授權(quán)的access_token + openID - 返回: " + strResult);
                  po = JacksonUtil.defaultInstance().json2pojo(strResult, Oauth2AccessTokenRespPo.class);
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          LOGGER.error("獲取網(wǎng)頁(yè)授權(quán)的access_token + openID失敗!!!");
      }

      return po;
  }
  
  /**
   * 接口返回參數(shù)PO
   * @author mac
   *
   */
  @JsonIgnoreProperties(ignoreUnknown = true)
  public static class Oauth2AccessTokenRespPo{
      //網(wǎng)頁(yè)授權(quán)接口調(diào)用憑證,注意:此access_token與基礎(chǔ)支持的access_token不同
      private String access_token;
      //access_token接口調(diào)用憑證超時(shí)時(shí)間掂铐,單位(秒)
      private Long expires_in;
      //用戶刷新access_token
      private String refresh_token;
      //用戶唯一標(biāo)識(shí),請(qǐng)注意揍异,在未關(guān)注公眾號(hào)時(shí)全陨,用戶訪問公眾號(hào)的網(wǎng)頁(yè),也會(huì)產(chǎn)生一個(gè)用戶和公眾號(hào)唯一的OpenID
      private String openid;
      //用戶授權(quán)的作用域衷掷,使用逗號(hào)(,)分隔
      private String scope;
      
      //錯(cuò)誤碼 - 錯(cuò)誤時(shí)返回
      private Integer errcode;
      //錯(cuò)誤信息 - 錯(cuò)誤時(shí)返回
      private String errmsg;
      
      public Oauth2AccessTokenRespPo(){}
      
      public String getAccess_token() {
          return access_token;
      }
      public void setAccess_token(String access_token) {
          this.access_token = access_token;
      }
      public Long getExpires_in() {
          return expires_in;
      }
      public void setExpires_in(Long expires_in) {
          this.expires_in = expires_in;
      }
      public String getRefresh_token() {
          return refresh_token;
      }
      public void setRefresh_token(String refresh_token) {
          this.refresh_token = refresh_token;
      }
      public String getOpenid() {
          return openid;
      }
      public void setOpenid(String openid) {
          this.openid = openid;
      }
      public String getScope() {
          return scope;
      }
      public void setScope(String scope) {
          this.scope = scope;
      }
      public Integer getErrcode() {
          return errcode;
      }
      public void setErrcode(Integer errcode) {
          this.errcode = errcode;
      }
      public String getErrmsg() {
          return errmsg;
      }
      public void setErrmsg(String errmsg) {
          this.errmsg = errmsg;
      }
      
  }

}

微信公眾號(hào)用戶信息Po(SnsapiUserinfo)

/**
* 微信公眾號(hào)用戶信息
* @author mac
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SnsapiUserinfo {
  
  //用戶的唯一標(biāo)識(shí)
  private String openid;
  //用戶昵稱
  private String nickname;
  //用戶的性別辱姨,值為1時(shí)是男性,值為2時(shí)是女性戚嗅,值為0時(shí)是未知
  private Integer sex;
  //用戶個(gè)人資料填寫的省份
  private String province;
  //普通用戶個(gè)人資料填寫的城市
  private String city;
  //國(guó)家雨涛,如中國(guó)為CN
  private String country;
  //用戶頭像,最后一個(gè)數(shù)值代表正方形頭像大信嘲(有0替久、46、64躏尉、96蚯根、132數(shù)值可選,0代表640*640正方形頭像)胀糜,用戶沒有頭像時(shí)該項(xiàng)為空颅拦。若用戶更換頭像,原有頭像URL將失效教藻。
  private String headimgurl;
  //用戶特權(quán)信息距帅,json 數(shù)組,如微信沃卡用戶為(chinaunicom)
  private List<String> privilege;
  //只有在用戶將公眾號(hào)綁定到微信開放平臺(tái)帳號(hào)后括堤,才會(huì)出現(xiàn)該字段碌秸。
  private String unionid;
  
  private String language;
  
  private String errcode;
  private String errmsg;
  
  
  public String getOpenid() {
      return openid;
  }
  public void setOpenid(String openid) {
      this.openid = openid;
  }
  public String getNickname() {
      return nickname;
  }
  public void setNickname(String nickname) {
      this.nickname = nickname;
  }
  public Integer getSex() {
      return sex;
  }
  public void setSex(Integer sex) {
      this.sex = sex;
  }
  public String getProvince() {
      return province;
  }
  public void setProvince(String province) {
      this.province = province;
  }
  public String getCity() {
      return city;
  }
  public void setCity(String city) {
      this.city = city;
  }
  public String getCountry() {
      return country;
  }
  public void setCountry(String country) {
      this.country = country;
  }
  public String getHeadimgurl() {
      return headimgurl;
  }
  public void setHeadimgurl(String headimgurl) {
      this.headimgurl = headimgurl;
  }
  
  public List<String> getPrivilege() {
      return privilege;
  }
  public void setPrivilege(List<String> privilege) {
      this.privilege = privilege;
  }
  public String getUnionid() {
      return unionid;
  }
  public void setUnionid(String unionid) {
      this.unionid = unionid;
  }
  public String getErrcode() {
      return errcode;
  }
  public void setErrcode(String errcode) {
      this.errcode = errcode;
  }
  public String getErrmsg() {
      return errmsg;
  }
  public void setErrmsg(String errmsg) {
      this.errmsg = errmsg;
  }
  public String getLanguage() {
      return language;
  }
  public void setLanguage(String language) {
      this.language = language;
  }
  
  
}

微信公眾號(hào)用戶信息 工具類(SnsapiUserinfoUtil)

/**
* 微信公眾號(hào)用戶信息 工具類
* @author mac
*
*/
@SuppressWarnings("deprecation")
@Component
public class SnsapiUserinfoUtil {

  private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

  final String openidUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

  /**
   * 獲得 微信公眾號(hào)用戶信息
   * @param accessToken
   * @param openid
   * @return
   */
  @SuppressWarnings({ "resource" })
  public SnsapiUserinfo getSnsapiUserinfo(String accessToken, String openid) {
      SnsapiUserinfo info = null;
      
      if(StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid)){
          LOGGER.info("微信公眾號(hào)用戶信息 工具類 - 獲得 微信公眾號(hào)用戶信息 - accessToken||openid 為空");
          return null;
      }

      // 微信API接口
      String url = openidUrl;
      url = url.replace("ACCESS_TOKEN", accessToken);
      url = url.replace("OPENID", openid);
      LOGGER.info("微信公眾號(hào)用戶信息 工具類 - 獲得 微信公眾號(hào)用戶信息 - url: " + url);

      HttpGet request = new HttpGet(url);
      HttpResponse response = null;
      try {
          HttpClient client = new DefaultHttpClient();
          response = client.execute(request);
          if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              String strResult = EntityUtils.toString(response.getEntity());
              if (!StringUtils.isEmpty(strResult)) {
                  info = JacksonUtil.defaultInstance().json2pojo(strResult, SnsapiUserinfo.class);
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          LOGGER.info("微信公眾號(hào)用戶信息 工具類 - 獲得 微信公眾號(hào)用戶信息 失敗");
      }

      return info;
  }
}
    1. 小程序內(nèi)通過web-view標(biāo)簽訪問“公眾號(hào)授權(quán)網(wǎng)頁(yè)url”接口返回的url, 引導(dǎo)用戶公眾號(hào)授權(quán)

貼代碼啦:

html:
   <div v-if="needPublicAccountAuth">
     <web-view :src="publicAccountAuthUrl" bindmessage="getMessage"></web-view>
   </div>

js:
     //進(jìn)入公眾號(hào)授權(quán)頁(yè)面
     authPublicAccount(){
       let that = this
       let param={}
       wx.request({
         url: api.wxPublic.return2OpenidUrl,
         data: param,
         method: 'POST',
         header: {
           'content-type': 'application/json'
         },
         success:function (res) {
           //打開webview
           that.$data.needPublicAccountAuth=true
           that.$data.publicAccountAuthUrl = res.data;
           
           //console.log(res.data)
           setTimeout(() => {
             console.log("公眾號(hào)授權(quán)完成")
             that.$data.needPublicAccountAuth=false
             that.$data.publicAccountAuthUrl = ''
           }, 5000);
             
         },
         fail:function (res) {
           wx.showToast({
             title:"獲得公眾號(hào)授權(quán)URL錯(cuò)誤",
             icon:"none",
             duration:2000
           })
         }
       })

     }

這樣,我們的小程序用戶與公眾號(hào)用戶就邏輯關(guān)聯(lián)起來啦

小程序用戶通過unionId關(guān)聯(lián)找到公眾號(hào)openid,進(jìn)而下發(fā)模版消息.

步驟:
  • 通過用戶的unionId關(guān)聯(lián)得到用戶公眾號(hào)openid
  • 通過用戶公眾號(hào)openid,下發(fā)模版消息(這里使用“統(tǒng)一服務(wù)消息接口”)


    統(tǒng)一服務(wù)消息接口
貼代碼啦:
/**
* 發(fā)送微信統(tǒng)一服務(wù)消息工具類
* 
* @author mac
*
*/
@SuppressWarnings("deprecation")
@Component
public class WechatMessageUtil {
  private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

  // 發(fā)送模版消息接口
  final String sendMessageUrl = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN";

  @Value("${wx.applet.appid}")
  String wxAppid;
  @Value("${wx.applet.secret}")
  String wxSecret;
  @Value("${wx.public.appid}")
  String publicAppid;
  @Value("${wx.public.secret}")
  String publicSecret;

  @Autowired
  WechatAccessTokenBuilder builder;

  /**
   * 發(fā)送模版信息
   * 
   * @param reqPo
   * @return
   * @throws JsonProcessingException 
   */
  @SuppressWarnings({ "resource" })
  public RespPo uniformSend(ReqPo reqPo) throws JsonProcessingException {
      RespPo respPo = null;
      LOGGER.info("發(fā)送公眾號(hào)模版信息 start");
      
      String accessToken = reqPo.getAccess_token();
      String reqPoJson = JacksonUtil.defaultInstance().pojo2json(reqPo);

      // 獲取小程序全局唯一后臺(tái)接口調(diào)用憑據(jù) 接口
      String url = sendMessageUrl.replace("ACCESS_TOKEN", accessToken);

      HttpPost request = new HttpPost(url);
      request.setEntity(new StringEntity(reqPoJson, ContentType.DEFAULT_TEXT.withCharset(Charset.defaultCharset())));
      request.setHeader(new BasicHeader("Content-Type", "application/json"));
      HttpResponse response = null;

      try {
          HttpClient client = new DefaultHttpClient();
          response = client.execute(request);
          if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              String strResult = EntityUtils.toString(response.getEntity());
              if (!StringUtils.isEmpty(strResult)) {
                  LOGGER.info("發(fā)送公眾號(hào)模版信息 - 返回: " + strResult);
                  respPo = JacksonUtil.defaultInstance().json2pojo(strResult, RespPo.class);
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          LOGGER.error("發(fā)送公眾號(hào)模版信息失敗!!!");
      }

      return respPo;
  }

  /**
   * 組裝發(fā)送模版信息所需的請(qǐng)求參數(shù)
   * 
   * @return
   * @throws Exception
   */
  public ReqPo packageReqPo(TmplBase base, String openId) throws Exception {
      ReqPo reqPo = new ReqPo();
      MpTemplateMsg mpTemplateMsg = new MpTemplateMsg();

      String accessToken = getAccessToken();
      if (StringUtils.isEmpty(accessToken)) {
          LOGGER.info("公眾號(hào)主體無法獲得有效的accessToken");
          return null;
      }

      reqPo.setTouser(openId);//用戶openid绍移,可以是小程序的openid,也可以是mp_template_msg.appid對(duì)應(yīng)的公眾號(hào)的openid
      reqPo.setAccess_token(accessToken);
      reqPo.setMp_template_msg(mpTemplateMsg);
      reqPo.setWeapp_template_msg(null);
      
      mpTemplateMsg.setAppid(publicAppid);// 公眾號(hào)appid
      mpTemplateMsg.setTemplate_id(base.tmplID);// template_id
      mpTemplateMsg.setData(base.getData());// 公眾號(hào)模板消息的數(shù)據(jù)
      mpTemplateMsg.setMiniprogram(base.miniprogram);// 公眾號(hào)模板消息所要跳轉(zhuǎn)的小程序
      mpTemplateMsg.setUrl(null);// 公眾號(hào)模板消息所要跳轉(zhuǎn)的url

      return reqPo;
  }

  /**
   * 獲得access_token
   * 
   * @return
   * @throws Exception
   */
  private String getAccessToken() {
      WechatAccessToken token = new WechatAccessToken(builder);
      return token.getAccessToken();
  }

}

/**
* 下發(fā)小程序和公眾號(hào)統(tǒng)一的服務(wù)消息 請(qǐng)求PO
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
* @author mac
*
*/
public class ReqPo {

  //接口調(diào)用憑證
  private String access_token;
  //用戶openid讥电,可以是小程序的openid登夫,也可以是mp_template_msg.appid對(duì)應(yīng)的公眾號(hào)的openid
  private String touser;
  //小程序模板消息相關(guān)的信息,可以參考小程序模板消息接口; 有此節(jié)點(diǎn)則優(yōu)先發(fā)送小程序模板消息
  private WeappTemplateMsg weapp_template_msg;
  //公眾號(hào)模板消息相關(guān)的信息允趟,可以參考公眾號(hào)模板消息接口;有此節(jié)點(diǎn)并且沒有weapp_template_msg節(jié)點(diǎn)時(shí)鸦致,發(fā)送公眾號(hào)模板消息
  private MpTemplateMsg mp_template_msg;
      
}

/**
* 返回值 PO
* @author mac
*
*/
public class RespPo {
  //錯(cuò)誤碼 0-成功
  private int errcode;
  //錯(cuò)誤信息
  private String errmsg;
}
/**
* 小程序模板消息相關(guān)的信息
* @author mac
*
*/
public class WeappTemplateMsg {
  
  //小程序模板ID
  private String template_id;
  //小程序頁(yè)面路徑
  private String page;
  //小程序模板消息formid
  private String form_id;
  //小程序模板數(shù)據(jù)
  private String data;
  //小程序模板放大關(guān)鍵詞
  private String emphasis_keyword;
}
/**
* 公眾號(hào)模板消息相關(guān)的信息
* @author mac
*
*/
public class MpTemplateMsg {

  //公眾號(hào)appid潮剪,要求與小程序有綁定且同主體
  private String appid;
  //公眾號(hào)模板id
  private String template_id;
  //公眾號(hào)模板消息所要跳轉(zhuǎn)的url
  private String url;
  //公眾號(hào)模板消息所要跳轉(zhuǎn)的小程序,小程序的必須與公眾號(hào)具有綁定關(guān)系
  private Miniprogram miniprogram;
  //公眾號(hào)模板消息的數(shù)據(jù)
  private Map<String, DataValue> data;
}
/**
* 公眾號(hào)模版消息 - 小程序跳轉(zhuǎn)PO
* @author mac
*
*/
public class Miniprogram {

  private String appid="wx2a8dd229406ae6ae";
  private String pagepath;
}
  • 微信小程序WechatAccessToken生成器
    由于 發(fā)送微信統(tǒng)一服務(wù)消息 , 需要使用到微信小程序的accessToken, 因此這里加上微信小程序WechatAccessToken生成器
/**
* 小程序access_token
* @author mac
*
*/
public class WechatAccessToken {

  private String accessToken;
  private Date builderTime;
  
   /**
    * 獲得 accessToken
    * @return
    */
   public String getAccessToken() {
       return accessToken;
   }
   /**
    * 獲得 builderTime
    * @return
    */
   public Date getBuilderTime() {
      return builderTime;
   }
   
   /**
    * 使用 內(nèi)部構(gòu)建類 構(gòu)建
    * @param builder
    * @throws Exception 
    */
   public WechatAccessToken(WechatAccessTokenBuilder builder){
      WechatAccessTokenBuilder build = builder.build();
      if(!StringUtils.isEmpty(build.accessToken)){
          this.accessToken = build.accessToken;
          this.builderTime = build.builderTime;
      }
   }
}
/**
* 微信小程序WechatAccessToken生成器
* @author mac
*
*/
@Component
@SuppressWarnings("deprecation")
public class WechatAccessTokenBuilder {
  private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

  @Value("${wx.applet.appid}")
  String appletAppid;
  @Value("${wx.applet.secret}")
  String appletSecret;
  
  //獲取access_token接口
  final String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
  
  public String accessToken;
  public Date builderTime;
   
   
  /**
   * 生成 WechatAccessToken
   * @return
   * @throws Exception 
   */
  public WechatAccessTokenBuilder build() {
      if(this.accessToken == null || this.builderTime == null){
          this.accessToken = getAccessToken();
          this.builderTime = new Date();
          return this;
      }
      
      //比較時(shí)間
      Calendar cal = Calendar.getInstance();   
       cal.setTime(this.builderTime);  
       cal.add(Calendar.HOUR, 1);//往后1小時(shí) 
       //cal.add(Calendar.MINUTE, 5);//往后5分鐘
       Date outTimeDate = cal.getTime();   
      Date nowDate = new Date();
      
      if(outTimeDate.after(nowDate)){
          return this;
      }else{
          this.accessToken = getAccessToken();
          this.builderTime = new Date();
          return this;
      }
      
   }
   
   /**
   * 獲取access_token
   * 
   * @return
   */
  @SuppressWarnings({ "resource"})
  private String getAccessToken() {
      
      // 獲取小程序全局唯一后臺(tái)接口調(diào)用憑據(jù) 接口 
      String url = getTokenUrl.replace("APPID", appletAppid);
      url = url.replace("APPSECRET", appletSecret);

      HttpGet request = new HttpGet(url);
      HttpResponse response = null;
      AccessTokenRespPo po = null;
      
      try {
          HttpClient client = new DefaultHttpClient();
          response = client.execute(request);
          if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              String strResult = EntityUtils.toString(response.getEntity());
              if (!StringUtils.isEmpty(strResult)) {
                  LOGGER.info("WechatAccessTokenBuilder.獲取access_token.返回: " + strResult);
                  po = JacksonUtil.defaultInstance().json2pojo(strResult, AccessTokenRespPo.class);
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          LOGGER.error("獲取微信小程序的access_token失敗!!!");
      }
      

      if (po != null && !"".equals(po.getAccess_token())) {
          return po.getAccess_token();
      } else {
          return null;
      }
  }

  /**
   * 接口返回參數(shù)PO
   * @author mac
   *
   */
  @JsonIgnoreProperties(ignoreUnknown = true)
  public static class AccessTokenRespPo {
      public String access_token;
      public int expires_in;
      public int errcode;
      public String errmsg;
      
      public AccessTokenRespPo(){}
      
      public String getAccess_token() {
          return access_token;
      }
      public void setAccess_token(String access_token) {
          this.access_token = access_token;
      }
      public int getExpires_in() {
          return expires_in;
      }
      public void setExpires_in(int expires_in) {
          this.expires_in = expires_in;
      }
      public int getErrcode() {
          return errcode;
      }
      public void setErrcode(int errcode) {
          this.errcode = errcode;
      }
      public String getErrmsg() {
          return errmsg;
      }
      public void setErrmsg(String errmsg) {
          this.errmsg = errmsg;
      }
      
  }
}
  • 發(fā)送模版消息Demo
/**
* 公眾號(hào)模版類
* @author mac
*
*/
public abstract class TmplBase {
  
  /**
   * 模版id
   */
  public String tmplID;
  /**
   * 模版參數(shù)
   */
  public Map<String, DataValue> data;
  
  /**
   * 公總號(hào)跳轉(zhuǎn)小程序
   */
  public Miniprogram miniprogram;
  
}
demo:
@Autowired
  WechatMessageUtil wechatMessageUtil;
  //小程序appid
  @Value("${wx.applet.appid}")
  String appletAppid;
  //公眾號(hào)appid
  @Value("${wx.public.appid}")
  String publicAppid;
  
  @Test
  public void test() throws Exception {
      
      //點(diǎn)擊模版消息跳轉(zhuǎn)參數(shù)
      Miniprogram miniprogram = new Miniprogram();
      miniprogram.setAppid(appletAppid);//appid需要線上有版本才能匹配對(duì)應(yīng)的路徑
      miniprogram.setPagepath("pages/Login");//要跳轉(zhuǎn)的路徑+參數(shù)
      
      //模版消息
      TestTmpl testTmpl = new TestTmpl(new DataValue("1"), new DataValue("2"), new DataValue("3"), new DataValue("4"), miniprogram);
      
      ReqPo reqPo = wechatMessageUtil.packageReqPo4Customer(testTmpl, publicAppid);
      RespPo uniformSend = wechatMessageUtil.uniformSend(reqPo);
      
      System.out.println(uniformSend);

  }
終于碼完字啦,呼~

看到這里,如果對(duì)你們有用,請(qǐng)鼓勵(lì)一下筆者吧
第一次碼字, 有錯(cuò)誤請(qǐng)見諒~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末分唾,一起剝皮案震驚了整個(gè)濱河市抗碰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绽乔,老刑警劉巖弧蝇,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異折砸,居然都是意外死亡看疗,警方通過查閱死者的電腦和手機(jī)睦授,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門两芳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人去枷,你說我怎么就攤上這事怖辆。” “怎么了删顶?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵竖螃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我逗余,道長(zhǎng)特咆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任猎荠,我火速辦了婚禮坚弱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘关摇。我一直安慰自己荒叶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布输虱。 她就那樣靜靜地躺著些楣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愁茁,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天蚕钦,我揣著相機(jī)與錄音,去河邊找鬼鹅很。 笑死嘶居,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的促煮。 我是一名探鬼主播邮屁,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼菠齿!你這毒婦竟也來了佑吝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤绳匀,失蹤者是張志新(化名)和其女友劉穎芋忿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疾棵,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戈钢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陋桂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逆趣。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗜历,靈堂內(nèi)的尸體忽然破棺而出宣渗,到底是詐尸還是另有隱情,我是刑警寧澤梨州,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布痕囱,位于F島的核電站,受9級(jí)特大地震影響暴匠,放射性物質(zhì)發(fā)生泄漏鞍恢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一每窖、第九天 我趴在偏房一處隱蔽的房頂上張望帮掉。 院中可真熱鬧,春花似錦窒典、人聲如沸蟆炊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涩搓。三九已至污秆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昧甘,已是汗流浹背良拼。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留充边,地道東北人庸推。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像浇冰,于是被迫代替她去往敵國(guó)和親予弧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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