微信公众号开发经验总结

1.                快捷访问

1.1   测试公众号注册:

http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

1.2   微信公众号开发指南:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432

1.3   微信全局返回码说明:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234

1.4   微信公众号接口权限说明:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084

2       注册一个公众号账户(也可先用测试公众号调试环境)

2.1              账户注册

2.1.1          正确填写下列信息

注意:

URL必须以http:// 或者https:// 开头,分别支持80端口和443端口;

Token必须为英文或者数字,长度为3-32字符;

2.1.2          开发过程中,需要注意接口的调用次数,

2.1.3          注意检查URL和JS接口安全域名,可以保持一致,也可以不一样,个人建议最好保持一致;

3      测试开发环境

Eg:

微信网页授权access_token和普通access_token区别

https://blog.csdn.net/benben_2015/article/details/79703508

微信获取用户信息的两个接口和两个ACCESS_TOKEN

https://www.cnblogs.com/sxmny/articles/4969119.html

2.1 记录全局定量(获取Token的两个定量,在微信公众号-账户信息处可以查询):

Appid                   应用ID

AppSecret          应用密匙

2.2 获取普通access_token(access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需要使用access_token)

调用接口:

URL:        https://api.weixin.qq.com/cgi-bin/token

参数:     grant_type : “client_credential”--------获取access_token填写client_credential                             Appid : Appid---------第三方用户唯一凭证

AppSecret : AppSecret-----------第三方用户唯一凭证密钥,即appsecret

返回参数:      access_token---------获取到的凭证

expires_in----------凭证有效事件,单位:秒

注:公众号对此接口的调用是有次数限制的,默认是2000,花钱可增加次数,获取的access_token的有效期默认是2小时,故而,在开发过程中,可将调用此接口的access_token设为全局变量,并将次接口的调用方法设为全局自动调用,独立出来;

Eg(将获取access_token的接口独立,并调整为100分钟自动刷新):

package com.gzh.api.utils;

import java.util.Map;

import java.util.Timer;

import java.util.TimerTask;

import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.fastjson.JSONObject;

import com.google.common.collect.Maps;

import com.jfinal.kit.Prop;

import com.jfinal.kit.PropKit;

import cn.hutool.http.HttpUtil;

/**

* access_token 自动刷新

* @author Administrator

*

*/

public class WxToken {

public static String globalTokenObject = "";//调取接口的返回值参数(access_token、expires_in)

public static String globalToken = "";// access_token

public static AtomicInteger atomicInteger = new AtomicInteger();//计数器,统计接口调用的次数

public static String appid = "**************";

public static String appsecret = "*************";

static {

new Timer().schedule(new TimerTask() {

@Override

public void run() {

Map<String,Object> paramMap = Maps.newHashMap();

paramMap.put("appid", appid);

paramMap.put("secret", appsecret);

paramMap.put("grant_type", "client_credential");

globalTokenObject = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);

JSONObject wxToken = JSONObject.parseObject(globalTokenObject);

globalToken = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxToken.getString("access_token") + "&type=jsapi");

atomicInteger.incrementAndGet();

}

}, 0, 2*50*60*1000);

}

}

2.3 获取用户Code/微信访问公众号地址

使用微信打开如下地址https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数:appid---------appid

redirect_uri-------系统路径,需与微信公中号绑定路径一致

response_type-------“code”字符串

scope------“snsapi_userinfo”字符串

state------“1”字符串

(注意这个接口中有个参数scope 默认有2个值snsapi_base和snsapi_userinfo,这个接口会根据scope 来生成不同的code并且获取不同作用的access_token ,不管scope传什么值都能在得到对应access_token的同时得到open_id, 如果你只需要得到opend_id 那使用snsapi_base参数到此结束了,如果需要获取用户的其他信息比如 昵称 地址 就要snsapi_userinfo 会弹出授权)

eg:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx97967746637ee7c4&redirect_uri=http://www.HuaLuo.com/gzh/gzh/app.html&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect

直接在微信中访问,系统中用如下方法接收:

//获取用户code       --var code = getCode(‘code’);

Function getCode(name){

var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");

var r = window.location.search.substr(1).match(reg);

if (r != null) return unescape(r[2]); return null;

}

即可拿到用户Code;

2.4 获取openid

简单的实现微信获取openid

https://blog.csdn.net/z880698223/article/details/78485243/

1.调用https://open.weixin.qq.com/connect/oauth2/authorize接口获取到code

《参数2.3》

2.得到code作为一个参数调用https://api.weixin.qq.com/sns/oauth2/access_token接口获取到openid 《参照2.5》

2.5 获取微信网页授权access_token

调用接口:

URL:          https://api.weixin.qq.com/sns/oauth2/access_token

参数:

grant_type : “authorization_code”--------填写client_credential

Appid : Appid---------第三方用户唯一凭证

AppSecret : AppSecret-----------第三方用户唯一凭证密钥,即appsecret

Code:Code----------标识不同的用户code 《参照2.3》

返回参数:

Openid

access_token

2.6 获取微信用户信息

EG:

https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

调用接口:

URL:        https://api.weixin.qq.com/sns/userinfo

参数:

Openid-----《参照2.5》

Lang----“zh_CN”字符串

access_token------《参照2》>(注:此access_token网上资料说需要普通access_token,及调用《2.2》接口返回的普通access_token,但是实际中发现调用《2.5》接口返回的网页授权access_token也可以拿到用户信息,故而实际开发中可换着调用调试,个人建议使用普通access_token,因为开文提出将此参数定义为全局变量,故而对代码一致性维护较方便)

返回参数:

Subscribe--------用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息

Openid----------用户的标识,对当前公众号唯一

Nickname-----------用户的昵称

Sex----------用户的性别,值为1时是男性,值为2时是女性,值为0时是未知

City----------用户所在城市

Country-------------用户所在国家

Province-----------用户所在省份

Language---------用户的语言,简体中文为zh_CN

Headimgurl-----------用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效

subscribe_time-------------用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间

unionid------------只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段

remark-----------公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注

groupid ------------- 用户所在的分组ID(兼容旧的用户分组接口)

tagid_list ------------用户被打上的标签ID列表

subscribe_scene----------- 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他

qr_scene ------------二维码扫码场景(开发者自定义)

qr_scene_str------------ 二维码扫码场景描述(开发者自定义)

2.7 调用微信JS-SDK,获取jsapi_ticket

微信公众平台jsapi开发教程(1)获取:

https://blog.csdn.net/linfanhehe/article/details/52354848

生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。《参照2.2》和我们获取普通access_token类似,由于有访问次数的限制,我们将次参数调为全局自动触发;

接口:

URL: https://api.weixin.qq.com/cgi-bin/ticket/getticket

参数: access_token-----《参照2.2》

Type----“jsapi”字符串

返回参数:ticket------------调用微信jsapi的凭证票

2.8 初始化微信config配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。

微信调取音频、视频、分享等接口前,需要先初始化验证

JS:

app.ajax('/wxInitConfig.action',{'url':location.href},function(configDate){

wx.config({

debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

appId: configDate.appid, // 必填,公众号的唯一标识

timestamp: configDate.timestamp, // 必填,生成签名的时间戳

nonceStr: configDate.nonceStr, // 必填,生成签名的随机串

signature: configDate.signature,// 必填,签名,见附录1

jsApiList: ['onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareTimeline','onMenuShareAppMessage','startRecord','stopRecord','onVoiceRecordEnd','playVoice','stopVoice','onVoicePlayEnd','uploadVoice','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

});

});

ACTION:

public void wxInitConfig(){

JSONObject wxToken = JSONObject.parseObject(WxToken.globalToken);《参照2.2代码,获取全局ticket》

String jsapi_ticket = wxToken.getString("ticket");

//此处如果没有正常获取到jsapi_ticket,请查看返回码,在微信公众号中查找错误信息

//前台传过来的当前URL访问路径

String url = Dto.getParam().getString("url");

//算法生成时间戳、签名等信息,见下列代码,直接附源码,不多说

Map<String, String> ret = Sign.sign(jsapi_ticket, url);

for (@SuppressWarnings("rawtypes") Map.Entry entry : ret.entrySet()) {

System.err.println(entry.getKey().toString() + " : " + entry.getValue());

Dto.getResult().set(entry.getKey().toString(), entry.getValue());

}

Dto.getResult().set("appid", wxProp.get("appid"));

this.renderJson(Dto.getResult().getData());

}

///算法生成时间戳、签名等信息/

过程中可能调用MessageDigest 包,请自行Baidu下载

package com.gzh.api.utils;

import java.util.UUID;

import java.util.Map;

import java.util.HashMap;

import java.util.Formatter;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.io.UnsupportedEncodingException;

public class Sign {

public static void main(String[] args) {

String jsapi_ticket = "jsapi_ticket";

// 注意 URL 一定要动态获取,不能 hardcode

String url = "http://example.com";

Map<String, String> ret = sign(jsapi_ticket, url);

for (Map.Entry entry : ret.entrySet()) {

System.out.println(entry.getKey() + ", " + entry.getValue());

}

};

public static Map<String, String> sign(String jsapi_ticket, String url) {

Map<String, String> ret = new HashMap<String, String>();

String nonce_str = create_nonce_str();

String timestamp = create_timestamp();

String string1;

String signature = "";

//注意这里参数名必须全部小写,且必须有序

string1 = "jsapi_ticket=" + jsapi_ticket +

"&noncestr=" + nonce_str +

"&timestamp=" + timestamp +

"&url=" + url;

System.out.println(string1);

try

{

MessageDigest crypt = MessageDigest.getInstance("SHA-1");

crypt.reset();

crypt.update(string1.getBytes("UTF-8"));

signature = byteToHex(crypt.digest());

}

catch (NoSuchAlgorithmException e)

{

e.printStackTrace();

}

catch (UnsupportedEncodingException e)

{

e.printStackTrace();

}

ret.put("url", url);

ret.put("jsapi_ticket", jsapi_ticket);

ret.put("nonceStr", nonce_str);

ret.put("timestamp", timestamp);

ret.put("signature", signature);

return ret;

}

private static String byteToHex(final byte[] hash) {

Formatter formatter = new Formatter();

for (byte b : hash)

{

formatter.format("%02x", b);

}

String result = formatter.toString();

formatter.close();

return result;

}

private static String create_nonce_str() {

return UUID.randomUUID().toString();

}

private static String create_timestamp() {

return Long.toString(System.currentTimeMillis() / 1000);

}

}

2.9 调用微信JS-SDK实例(以录音为例);

参考微信开发说明文档

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

/* 开启录音*/

wx.startRecord({

success:function() {

alert(“接口调用成功”);

},

Fail:function(){

alert(“接口调用失败”);//可以检查access_token权限或者弹出调试,具体问题具体分析《参照2.8》,开启调试模式

},

Cancel:function(){

alert(“用户拒绝提供录音权限”);

}

});

/* 停止录音*/

wx.stopRecord({

success: function (res) {

var localId = res.localId;

}
});

/* 监听录音自动停止(由于微信有录音时限限制,一次录音最大时长为1分钟,若超过此时=时限未停止录音,则执行此接口方法)*/

wx.onVoiceRecordEnd({// 录音时间超过一分钟没有停止的时候会执行 complete 回调complete: function (res) {var localId = res.localId;}});

注:我在此处有一个录音时长大于一分钟的问题,目前没有好的解决方案,楼主在此处的解决方案是:当执行上述大于一分钟回调时,重新开启录音接口,直至手动关闭录音;将几个录音的localId存起来,一并发至后台获取录音文件,合成一份音频文件,合成代码网上找,但这个方法有一个Bug,在重新开启录音,即转换的这个时间段,有大概1~2秒的录音失效期;

 
/* 播放录音 */
wx.playVoice({localId: '' // 需要播放的音频的本地ID,由stopRecord接口获得});
 
/* 暂停播放录音 */
wx.pauseVoice({localId: '' // 需要暂停的音频的本地ID,由stopRecord接口获得});
/* 停止播放录音 */
wx.stopVoice({localId: '' // 需要停止的音频的本地ID,由stopRecord接口获得});
/* 监听录音播放完毕接口 */
wx.onVoicePlayEnd({success: function (res) {var localId = res.localId; // 返回音频的本地ID}});
 
/* 上传录音接口 */
wx.uploadVoice({localId: '', // 需要上传的音频的本地ID,由stopRecord接口获得isShowProgressTips: 1, // 默认为1,显示进度提示success: function (res) {var serverId = res.serverId; // 返回音频的服务器端ID}});
备注:上传语音有效期3天,可用微信多媒体接口下载语音到自己的服务器,此处获得的 serverId 即 media_id,参考文档 .目前多媒体文件下载接口的频率限制为10000次/天,如需要调高频率,请登录微信公众平台,在开发 - 接口权限的列表中,申请提高临时上限。
 
根据serverId获取微信服务器上的录音文件;
调用接口:
URL: http://file.api.weixin.qq.com/cgi-bin/media/get
参数: access_token------参照《2.2》
media_id----------serverId
返回参数是文件流,需自行解析;附实例;
Eg:

/* (non-Javadoc)

* @see com.gzh.api.service.ReciteContentService#saveRecite(com.sdp.core.dt.DtoParam)

*/

@Override

public DtoResult saveRecite(DtoParam param) throws Exception{

/** 参数接收*/

String contentName = Dto.getParam().getString("contentName");//诗词名称

String contentId = Dto.getParam().getString("contentId");//诗词ID

String fansId = Dto.getParam().getString("fansId");//粉丝ID

String serverId = Dto.getParam().getString("serverId");

Record isFans = api.findFirst("select * from gzh_personalcenter where loginid = ?", fansId);

if(isFans != null && isFans.get("department") != null && !isFans.get("department").equals("")){

String[] serverIds = Str.isNotAnyEmpty(serverId)?serverId.split(","):null;/* 当传过来的serverId有效时执行*/

if(serverIds != null && serverIds.length > 0){

System.err.println("正在上传录音文件...");

long time1 = System.currentTimeMillis();

/* 删除旧录音文件及数据*/

List<Record> deleteList = api.find("select * from gzh_recitecontent where fansid = ? and contentid = ?", fansId, contentId);

for(Record re : deleteList){

File file = new File(fileRoot + re.getStr("path"));

if(file.exists()) file.delete();

api.deleteById("gzh_recitecontent", re.getStr("id"));

}

//String token = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);

JSONObject wxToken = JSONObject.parseObject(WxToken.globalTokenObject);

List<String> tempRecites = new ArrayList<>();/* amr文件集合*/

List<String> newRecites = new ArrayList<>();/* mp3文件集合*/

/* 进行临时目录创建*/

String tempResPath = fileRoot + tempPath;

if(!new File(tempResPath).exists()) new File(tempResPath).mkdirs();

/* 进行录音文件目录创建*/

String realResPath = fileRoot + fileStore + recitePath + "/" + contentId + "/" + fansId;//文件实际存储目录

if(!new File(realResPath).exists()) new File(realResPath).mkdirs();//先进行目录创建操作

int size = 0;/* 记录所有文件输入流大小*/

/* 定义输出流写入参数*/

FileOutputStream fileOutputStream = null;

InputStream inputStream = null;

String fileName = ID.get();/* 文件名称*/

/* 将文件分批取出并保存为MP3文件*/

for(int i = 0; i < serverIds.length; i++){

try{

/* 从微信服务器获取录音文件,格式为amr*/

String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token="

+ wxToken.getString("access_token") + "&media_id=" + serverIds[i];

URL urlGet = new URL(url);

HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();

http.setRequestMethod("GET"); // 必须是get方式请求

http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

http.setDoOutput(true);

http.setDoInput(true);

//               System.setProperty("sun.net.client.defaultConnectTimeout", "3000");// 连接超时30秒

//               System.setProperty("sun.net.client.defaultReadTimeout", "3000"); // 读取超时30秒

http.connect();

// 获取文件转化为byte流

inputStream = http.getInputStream();

size += inputStream.available();

String tempFileName = tempResPath + "/" + fileName + (i+1) + ".amr";

String recitFileName = tempResPath + "/" + fileName + (i+1) + ".mp3";

if(serverIds.length == 1){

recitFileName = realResPath + "/" + fileName + ".mp3";

}

tempRecites.add(tempFileName);

newRecites.add(recitFileName);

byte[] data = new byte[1024 * 1024];

int len = 0;

fileOutputStream = new FileOutputStream(tempFileName);

while ((len = inputStream.read(data)) != -1) {

fileOutputStream.write(data, 0, len);

}

/* 关闭流资源*/

if(inputStream != null) inputStream.close();

if(fileOutputStream != null) fileOutputStream.close();

/* amr格式转换成mp3格式*/

File source = new File(tempFileName);

File target = new File(recitFileName);

AudioAttributes audio = new AudioAttributes();

Encoder encoder = new Encoder();

audio.setCodec("libmp3lame");

EncodingAttributes attrs = new EncodingAttributes();

attrs.setFormat("mp3");

attrs.setAudioAttributes(audio);

encoder.encode(source, target, attrs);/* 执行转换*/

}catch(Exception e) {

System.err.println(e.getMessage());

}

}

System.err.println("输入流大小:" + size);

File file = new File(realResPath + "/" + fileName + ".mp3");

if(newRecites.size() > 1){

try {

combine(file.getPath(), newRecites);

} catch (Exception e) {

System.err.println(e.getMessage());

}

}

System.err.println("音频大小:" + file.length()/1024 + "KB");

//信息保存数据库表

Record record = new Record();

record.set("id", ID.get());

record.set("contentname", contentName);//诗词名称

record.set("contentid", contentId);//诗词ID

record.set("fansid", fansId);//粉丝ID

record.set("path", fileStore + recitePath + "/" + contentId + "/" + fansId + "/" + fileName + ".mp3");//吟诵录音路径

record.set("uploaddate", new Timestamp(new java.util.Date().getTime()));//吟诵时间

api.save(getTableName(), record);

long time2 = System.currentTimeMillis();

System.out.println("上传时间:" + (time2 - time1)/1000 + "s");

/* 删除临时文件*/

for(int i = 0; i < tempRecites.size(); i++){

File tempFile = new File(tempRecites.get(i));

if(tempFile.exists()){

System.out.println("删除AMR临时文件结果[" + tempRecites.get(i) + ":" + tempFile.delete() + "]");

}

if(tempRecites.size() > 1){

tempFile = new File(newRecites.get(i));

if(tempFile.exists()){

System.out.println("删除MP3临时文件结果[" + newRecites.get(i) + ":" + tempFile.delete() + "]");

}

}

}

}

}else{

Dto.getResult().setSuccessStatus("请完善个人信息");

}

return Dto.getResult();

}

/*将多个MP3文件合并*/

private static boolean combine(String outFile, List<String> inFiles) throws Exception{

File[] files = new File[inFiles.size()];

for(int i = 0; i < files.length; i++){

files[i] = new File(inFiles.get(i));

}

FileInputStream fis = null;

FileOutputStream fos = new FileOutputStream(outFile, true);//合并其实就是文件的续写,写成true

for (int i = 0; i < files.length; i++){

fis = new FileInputStream(files[i]);

int len = 0;

for (byte[] buf = new byte[1024 * 1024]; (len = fis.read(buf)) != -1;){

fos.write(buf, 0, len);

}

fis.close();

}

fos.close();

return true;

      }
 
/* 下载录音接口 */
wx.downloadVoice({serverId: '', // 需要下载的音频的服务器端ID,由uploadVoice接口获得isShowProgressTips: 1, // 默认为1,显示进度提示success: function (res) {var localId = res.localId; // 返回音频的本地ID}});
/* 此下载接口只是返回微信服务器端的文件localId ,然后调用播放接口读取localId ,但是微信服务器端的录音文件只保留3天,请知晓!*/
 
 
 
 

转载于:https://www.cnblogs.com/hualuo-sign/p/9732719.html

微信公众号开发经验总结相关推荐

  1. 微信公众号开发系列教程一(调试环境部署)

    http://www.cnblogs.com/zskbll/p/4074855.html 目录 C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试 ...

  2. 微信公众号开发实战 | 01:环境配置

    文章目录 前言 配置Node开发环境 安装Node.js 安装TypeScript 安装localtunnel 安装IDE 实现一个简单的自动回复功能 前言 在本系列的博客中我们将从零开始搭建一个基于 ...

  3. 通过微信公众号远程控制设备STM32+NB模组方案

    想要实现远程控制,无非就是三端的通信,发送设备端->服务器->接收设备端,服务器端可以选用一些常用的云服务器,阿里.百度等等.接收端就是实现控制的设备,发送设备端一般就是APP端.网页端等 ...

  4. 微信公众号服务器配置但没有回调,微信公众号 “服务器配置” 踩坑记

    前言 今天工作的时候,碰到一个极其无语的关于微信公众号的坑,为此,我语言攻击了腾讯的机器人客服.然而并没有卵用...万万没想到,我还是解决了这个问题,并记录下踩坑经历,希望能帮到一些朋友吧. 背景 今 ...

  5. 微信小程序「柒留言」 — 无留言公众号开通微信公众号留言功能(建议收藏)

    「柒留言」小程序留言助手使用指南(接近原生界面) 前言 从去年 3 月以后新公众号就没得留言功能了,新申请的微信公众号没有留言功能,没有留言就无法跟读者进行互动,写出去的文章得不到反馈,着实感觉有蛮难 ...

  6. 微信小程序 「柒留言」 — 实现微信公众号留言功能(限时免费入驻,建议收藏)...

    「柒留言」小程序留言助手使用指南(接近原生界面) 前言 从去年 3 月以后新公众号就没得留言功能了,新申请的微信公众号没有留言功能,没有留言就无法跟读者进行互动,写出去的文章得不到反馈,着实感觉有蛮难 ...

  7. 视频教程-SpringBoot微信公众号开发-微信开发

    SpringBoot微信公众号开发 就职于国内知名在线互联网旅游公司,10+互联网开发经验,精通前后端开发 刘志强 ¥149.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免 ...

  8. 是时候给你的微信公众号请一位专属客服了

    1.为什么要写这篇文章? 一是因为经常逛知乎,发现好些知乎大神的签名上都放的自己的微信公众号,但是关注之后就回一句简单的谢谢关注之类的话,缺乏互动性.二来是之前网盘共享了一堆本科生的毕设,为了防止别有 ...

  9. 视频教程-微信公众号开发-微信开发

    微信公众号开发 云知梦创始人,国际架构师,11年互联网培训和开发经验,曾在港电讯盈科.北大青鸟集团.远大教育.北京易第优教育等公司任职曾获得美国红帽RHCA构架师和RHCDS数据中心讲师,在国内排名第 ...

最新文章

  1. Java使用itext生成Pdf
  2. Python实现信息自动配对爬虫排版程序
  3. AFNetworking 源代码分析
  4. 【转】接口测试技术介绍
  5. SpingBoot中使用MyBatis和pagehelper实现数据的增删改查和分页
  6. java中间件_Java技术分享:一致性更强的分布式数据库中间件
  7. java map存储对象_JAVA:查找存储在hashMap中的对象的最佳性能方法
  8. python计算圆的面积
  9. 基于mxnet的Regression问题Kaggle比赛代码框架
  10. 第5节 三个败家子(5)——刘封,被封印的秘密
  11. 冈萨雷斯《数字图像处理》读书笔记(三)——空间滤波
  12. python读取邮箱邮件_Python读取邮箱邮件
  13. 类Shiro权限校验框架的设计和实现
  14. 算法笔记(23)网格搜索及Python代码实现
  15. apereo cas开发_CAS 之 Apereo CAS 简介(一)
  16. SCDM学习笔记(1)
  17. 马云选择了西雅图模式,你家公司选硅谷还是西雅图?
  18. wamp5数据库密码修改
  19. 语法分析器之递归子程序法
  20. 云桌面到底是干什么的?

热门文章

  1. HTML DOM之节点操作方法(2)
  2. 安卓手机阅读器_乐应用|安卓手机本地阅读的不二之选
  3. 133-PHP子类无法重写父类private同名函数
  4. 简单理解bash和常规操作
  5. [修正] Berlin 10.1 支持 iPhone 4 (iOS v7.x)
  6. 为什么构造函数不能是虚函数
  7. IOS6 新特性之UIRefreshControl
  8. 黑马Java学习笔记之-----集合框架
  9. nHibernate Mapping By Code - Introduction
  10. android 微信支付,body为中文字符,签名错误