Wechattty Project — это среда разработки на основе JAVA для общедоступных учетных записей WeChat (включая учетные записи служб и учетных записей подписки) и корпоративных учетных записей WeChat. Хорошо инкапсулированный API позволяет разработчикам сосредоточиться на разработке бизнес-логики и повысить эффективность разработки.
Maven здесь используется для введения зависимостей.
<dependency>
<groupId>space.chensheng.wechatty</groupId>
<artifactId>wechatty-mp</artifactId>
<version>2.0.0</version>
</dependency>
MpAppContext
— это унифицированный вход для вызова API общедоступной учетной записи, который инициализируется с помощью WechatMpBootstrap
.
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
MpAppContext mpAppContext = bootstrap.build();
Если проект управляется с помощью Spring, можно реализовать FactoryBean
для инициализации MpAppContext
для последующего использования.
@Component
public class MpAppContextFactoryBean implements FactoryBean<MpAppContext> {
@Override
public MpAppContext getObject() throws Exception {
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
return bootstrap.build();
}
@Override
public Class<?> getObjectType() {
return MpAppContext.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
Существует два метода настройки: один —配置文件
, другой — JAVA代码配置
. Приоритет JAVA代码配置
выше, чем у配置文件
.
Создайте новый файл конфигурации wechat-mp.properties и поместите его в путь к классам проекта. Например, в проекте maven этот файл можно поместить в каталог src/main/resources
. Общая конфигурация следующая:
token=thisIsToken
aesKey=thisIsAesKey
appId=thisIsYourAppId
appSecret=thisIsAppSecret
После инициализации MpAppConetxt
вызовите метод customizeWechatContext
WechatMpBootstrap
для настройки.
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.customizeWechatContext(new MpWechatContextCustomizer() {
@Override
public void customize(MpWechatContext wechatContext) {
wechatContext.setToken("thisIsToken");
wechatContext.setAesKey("thisIsAeskey");
wechatContext.setAppId("thisIsAppId");
wechatContext.setAppSecret("thisIsAppSecret");
}
});
Обязательные параметры | иллюстрировать |
---|---|
жетон | Токен официального аккаунта можно просмотреть на фоне официального аккаунта. |
aesKey | Ключ, используемый для шифрования, можно просмотреть в административной части официального аккаунта. |
идентификатор приложения | AppId официальной учетной записи можно просмотреть на фоне официальной учетной записи. |
appSecret | AppSecret официального аккаунта можно просмотреть на заднем плане официального аккаунта. |
Дополнительные параметры | иллюстрировать |
---|---|
включитьCryptedMode | Включить ли режим шифрования обратного вызова, по умолчанию — true. Если он включен, вам необходимо загрузить файл политики неограниченных разрешений JCE, чтобы охватить соответствующие файлы в jdk. Подробности см. в примерах распространенных ошибок в WeChat. |
автообновлениеAccessToken | Следует ли автоматически обновлять access_token при возникновении ошибки, связанной с access_token. По умолчанию установлено значение false. Приложение может обновлять его с помощью запланированных задач, которые будут подробно описаны позже. |
AccessTokenStrategyClass | Стратегия доступа access_token, по умолчанию — space.chensheng.wechatty.common.http.MemoryAccessTokenStrategy, сохраняет access_token в памяти, и приложение может реализовать свою собственную стратегию доступа, например, сохранить ее в базе данных, которая будет представлена в разделе подробности позже. |
payKey | Платежный ключ WeChat |
payCertFile | Путь к файлу платежного сертификата WeChat |
payCertPassword | Пароль платежного сертификата WeChat |
payMchId | Идентификатор платежного продавца WeChat |
payClientIp | Вызовите IP аппарата для оплаты |
пулингхттппроксиенабле | Запрашивать ли сервер WeChat через прокси-сервер, значение по умолчанию — false. |
пулингHttpProxyHostname | Имя хоста прокси-сервера, например www.chensheng.space. |
пулHttpProxyPort | Порт прокси-сервера |
пулингHttpProxyUsername | Имя пользователя прокси-сервера |
пулHttpProxyPassword | Пароль прокси-сервера |
пулHttpMaxPerRoute | Максимальное количество одновременных подключений на одну ссылку в пуле http-соединений, по умолчанию — 50. |
пулHttpMaxTotal | Максимальное количество одновременных подключений в пуле http-соединений, по умолчанию 200. |
пулингHttpSocketTimeoutMillis | Количество миллисекунд для таймаута сокета, по умолчанию 10000. |
пулингHttpConnectTimeoutMillis | Количество миллисекунд для подключения к таймауту сервера WeChat, по умолчанию 10000. |
пулингHttpConnectionRequestTimeoutMillis | Получите миллисекунды таймаута соединения из пула http-соединений, по умолчанию 10000. |
пулингHttpTcpNoDelay | Включить ли tpcNoDelay, по умолчанию true |
mpAppContext.getAccessTokenFetcher().updateAccessToken()
, обычно раз в 1,5 часа, поскольку срок действия access_token составляет 2 часа.accessTokenStrategyClass=your.package.name.YourAccessTokenStrategy
в wechat-mp.properties. Ниже представлена стратегия доступа к базе данных accesss_token: import space . chensheng . wechatty . common . http . AccessTokenStrategy ;
//因为这个策略类的实例化不是通过Spring来管理的,所以在这个类中不能使用Autowired来注入bean,
//要通过ApplicationContext#getBean方法来获取。
public class DatabaseAccessTokenStrategy implements AccessTokenStrategy {
//将access_token存到数据库中去
@ Override
public void doSave ( String accessToken ) {
TokenService tokenService = ApplicationContextUtil
. getApplicationContext (). getBean ( TokenService . class );
tokenService . doSave ( accessToken );
}
//从数据库中取出access_token
@ Override
public String doQuery () {
TokenService tokenService = ApplicationContextUtil
. getApplicationContext (). getBean ( TokenService . class );
return tokenService . doQuery ();
}
}
Когда MpAppContext
инициализирован, добавьте прослушиватель сообщений через WechatMpBootstrap
для получения сообщений (прослушиватель сообщений будет представлен позже):
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
bootstrap.addMsgListener(new SubscribeEventListener());
bootstrap.addMsgListener(new UnsubscribeEventListener());
Если вы установили URL-адрес обратного вызова в фоновом режиме официальной учетной записи WeChat, сервер WeChat отправит запрос GET на этот URL-адрес для проверки. Разработчику необходимо обработать этот запрос в веб-приложении. Ниже приведен пример проверки SpringMVC:
@RestController
@RequestMapping(value = "/wechat-mp")
public class CallbackController extends BaseController{
@Autowired
private MpAppContext mpAppContext;
//验证请求,并回复字符串
@RequestMapping(value = "/callback", method = RequestMethod.GET)
public String verify(String msg_signature, String timestamp, String nonce, String echostr) {
String reply = mpAppContext.getCallbackModeVerifier().verify(msg_signature, timestamp, nonce, echostr);
return reply;
}
}
После проверки запроса обратного вызова режим обратного вызова фактически включается. Если пользователь отправляет сообщение в официальную учетную запись, сервер WeChat отправит запрос POST на URL-адрес обратного вызова и перенаправит сообщение на этот URL-адрес. Разработчику необходимо обработать этот запрос в веб-приложении. Ниже приведен пример SpringMVC. (и предыдущий пример проверки включения обратного вызова в контроллере):
@RestController
@RequestMapping(value = "/wechat-mp")
public class CallbackController extends BaseController{
@Autowired
private MpAppContext mpAppContext;
//接收回调消息,并回复相应xml消息
@RequestMapping(value = "/callback", method = RequestMethod.POST)
public String verify(String msg_signature, String timestamp, String nonce) {
//postBody是请求体内容,String格式,开发者可以通过HttpServletRequest来解析
String replyXml = mpAppContext.getMpMessageDispatcher().dispatch(msg_signature(), timestamp, nonce, postBody);
return replyXml;
}
}
Разработчики могут прослушивать определенные типы сообщений, наследуя space.chensheng.wechatty.common.message.MessageListener
. Вот пример прослушивания текстовых сообщений, отправленных пользователями:
public class TextMessageListener extends MessageListener<TextInboundMessage> {
@Override
protected ReplyMessage onMessage(TextInboundMessage message) {
String content = message.getContent();
//根据消息内容来回复用户
if ("1".equals(content)) {
TextReplyMessage replyMsg = new TextReplyMessage();
replyMsg.setContent("this is reply message content");
replyMsg.setFromUserName(message.getToUserName());
replyMsg.setToUserName(message.getFromUserName());
replyMsg.setCreateTime(System.currentTimeMillis());
return replyMsg;
}
//返回null表示不回复用户
return null;
}
}
информация | иллюстрировать |
---|---|
Текстинбаундмессаже | текстовое сообщение |
ИзображениеInboundMessage | Графическое сообщение |
LinkInboundMessage | Перейти к графическому сообщению |
МестоположениеВходящее сообщение | Поделиться информацией о местоположении |
КороткоеВидеоВходящееСообщение | Небольшое видеообращение |
ВидеоВходящее сообщение | видео сообщение |
ГолосовоеВходящееСообщение | голосовое сообщение |
НажмитеСобытиеСообщение | Нажмите на обычное сообщение меню. |
ПосмотретьСобытиеСообщение | Нажмите, чтобы перейти к сообщению меню ссылки |
МестоположениеСобытиеСообщение | Сообщения о событиях местоположения |
ПодписатьсяСобытиеСообщение | Пользователи следят за новостями паблик-аккаунта |
ОтписатьсяСобытиеСообщение | Не забудьте отписаться от сообщений общедоступного аккаунта. |
ScanEventMessage | Пользователь сканирует сообщение с QR-кодом |
MassSendJobFinishEventMessage | Отчет о завершении отправки группового сообщения |
информация | иллюстрировать |
---|---|
ТекстОтветСообщение | текстовый ответ |
ИзображениеОтветитьСообщение | Изображение ответа |
МузыкаОтветитьСообщение | музыка ответ |
НовостиОтветСообщение | Графический ответ |
ВидеоОтветСообщение | видеоответ |
ГолосОтветСообщение | Голосовой ответ |
Официальные учетные записи могут активно отправлять пользователям сообщения, в том числе групповые сообщения и сообщения службы поддержки клиентов. Все сообщения отправляются единообразно с помощью space.chensheng.wechatty.mp.message.MpMessageSender
.
TextMassMessage message = new TextMassMessage();
message.setIsToAll(true);
message.setContent("群发消息测试");
mpAppContext.getMpMessageSender().send(message, 3);
Тип группового сообщения | иллюстрировать |
---|---|
ТекстМассаСообщение | Массовый текст |
ИзображениеМассаСообщение | Отправка группы изображений |
MpnewsМассаСообщение | Групповая отправка изображений и текста внутри WeChat |
MpvideoМассаСообщение | Групповая отправка видео |
ГолосМассаСообщение | Голосовая групповая отправка |
WxcardМассаСообщение | Массовое распространение карт WeChat и купонов |
TextCsMessage message = new TextCsMessage();
message.setToUser("thisIsUserOpenId");
message.setContent("客服消息测试 n 212");
mpAppContext.getMpMessageSender().send(message, 3);
Тип сообщения службы поддержки клиентов | иллюстрировать |
---|---|
ТекстCsMessage | Текстовая служба поддержки клиентов |
ИзображениеCsСообщение | Изображение обслуживания клиентов |
MpnewsCsСообщение | Графическое обслуживание клиентов в WeChat |
НовостиCsСообщение | Внешняя графическая служба поддержки клиентов |
ВидеоCsСообщение | Видео обслуживание клиентов |
ГолосCsСообщение | Голосовое обслуживание клиентов |
WxcardCsСообщение | Служба поддержки клиентов по картам и ваучерам WeChat |
Управление материалами в основном включает загрузку, запрос, изменение и удаление материалов. Типы материалов включают изображения, видео, голоса и графику.
Загрузка материалов завершается использованием соответствующего класса загрузки материалов. Ниже приведен пример загрузки изображений:
File image = new File("/this/is/image/path.jpg");
ImagePermanentMedia material = new ImagePermanentMedia(mpAppContext, image);
UploadResponse resp = material.upload();
Класс загрузки материала | иллюстрировать |
---|---|
ИзображениеПостоянныйМедиа | постоянная фотография |
ThumbPermanentMedia | Постоянная миниатюра |
ВидеоПостоянныйМедиа | постоянное видео |
ГолосПостоянныйМедиа | постоянный голос |
ПостоянныеНовости | Постоянные изображения и тексты |
ПостоянныйНовостиImg | Картинки в постоянной графике |
ИзображениеВременныеМедиа | Временное изображение |
Большой палецВременныйСМИ | Временная миниатюра |
ВидеоВременныеМедиа | временное видео |
ГолосВременныйМедиа | Временный голос |
Операция запроса материала выполняется с помощью классов инструментов space.chensheng.wechatty.mp.material.MaterialQuery
и space.chensheng.wechatty.mp.material.MaterialFinder
.
mpAppContext.getMaterialQuery().count()
mpAppContext.getMaterialQuery().listNews(int offset, int count)
mpAppContext.getMaterialQuery().listMedia(MediaType mediaType, int offset, int count)
mpAppContext.getMaterialFinder().findNews(String mediaId)
mpAppContext.getMaterialFinder().findPermanentVideo(String mediaId)
mpAppContext.getMaterialFinder().findTemporaryVideo(String mediaId)
mpAppContext.getMaterialFinder().downloadPermanentMedia(String mediaId, String saveDir, String fileName)
mpAppContext.getMaterialFinder().downloadTemporaryMedia(String mediaId, String saveDir, String fileName)
Операция удаления материала завершается с помощью класса инструмента space.chensheng.wechatty.mp.material.MaterialDeleter
.
mpAppContext.getMaterialDeleter().delete(String mediaId)
Генерация QR-кодов с параметрами завершается через класс инструмента space.chensheng.wechatty.mp.account.QRCodeCreator
.
mpAppContext.getQRCodeCreator().createTemporary(int expireSeconds, int sceneId)
mpAppContext.getQRCodeCreator().createPermanent(int sceneId)
mpAppContext.getQRCodeCreator().createPermanent(String sceneStr)
Запрос информации о пользователе реализован через UserInfoQuery
.
mpAppConext.getUserInfoQuery().get(String openId)
mpAppContext.getUserInfoQuery().batchGet(List<String> openIds)
Авторизация пользователя реализована через AuthHelper
.
auth access token
через код ссылки авторизации: mpAppContext.getAuthHelper().fetchAuthAccessToken(String code)
auth access token
: mpAppContext.getAuthHelper().refreshAuthAccessToken(String refreshAccessToken)
auth access token
: mpAppContext.getAuthHelper().fetchAuthUserInfo(String authAccessToken, String openId)
Ниже приведен псевдокод для авторизации пользователя:
public WxAuthLoginDto authAndLogin(String code) {
AuthAccessTokenResponse authResp = mpAppContext.getAuthHelper().fetchAuthAccessToken(code);
if (authResp == null || !authResp.isOk()) {
//授权失败,执行相应业务逻辑
return new WxAuthLoginDto("fail");
}
String openId = authResp.getOpenId();
AuthUserInfoResponse wxUserInfo = mpAppContext.getAuthHelper().fetchAuthUserInfo(authResp.getAccessToken(), authResp.getOpenId())
//根据微信用户信息在数据库里查找系统对应的用户,或新建一个用户
//进行登录相关业务逻辑处理
return new WxAuthLoginDto("success");
}
авторизация jsapi реализована через JsapiHelper
.
jsapi ticket
(вы можете использовать запланированные задачи, чтобы регулярно получать билеты и сохранять их в базе данных): mpAppContext.getJsapiHelper().fetchTicket()
mpAppContext.getJsapiHelper().generateSignature(String jsapiTicket, String nonceStr, long timestamp, String url)
При инициализации MpAppContext
вызовите метод enablePayCert()
WechatMpBootstrap
, чтобы включить оплату WeChat и настроить соответствующие параметры. (Конкретные параметры см. в модуле конфигурации)
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.enablePayCert();
mpAppContext.getPayHelper().sendRedPack(RedPackRequest request)
mpAppContext.getPayHelper().sendGroupRedPack(GroupRedPackRequest request)
mpAppContext.getPayHelper().transfers(TransfersRequest request)
mpAppContext.getPayHelper().unifiedOrder(UnifiedOrderRequest request)
mpAppContext.getPayHelper().parsePayNotify(String notifyContent)
mpAppContext.getPayHelper().validatePayNotify(PayNotifyResponse response)
mpAppContext.getPayHelper().orderQuery(OrderQueryRequest request)
mpAppContext.getPayHelper().closeOrder(CloseOrderRequest request)
mpAppContext.getPayHelper().shortUrl(String longUrl)
mpAppContext.getPayHelper().generateJsapiPayParams(String prepayId, PaySignType signType)
mpAppContext.getPayHelper().refund(RefundRequest request)
mpAppContext.getPayHelper().parseRefundNotify(String notifyContent)