init v1.0.0
BIN
image/07.png
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 547 KiB |
BIN
image/08.png
|
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 222 KiB |
BIN
image/10.png
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 111 KiB |
BIN
image/11.png
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 139 KiB |
28
pom.xml
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<name>ruoyi-ai</name>
|
<name>ruoyi-ai</name>
|
||||||
<url>https://gitee.com/ageerle/ruoyi-ai</url>
|
<url>https://gitee.com/ageerle/ruoyi-ai</url>
|
||||||
<description>AI助手后台管理系统</description>
|
<description>AI助手</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.0.0</revision>
|
<revision>1.0.0</revision>
|
||||||
@@ -242,16 +242,16 @@
|
|||||||
<version>${tencent.sms.version}</version>
|
<version>${tencent.sms.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>de.codecentric</groupId>
|
<!-- <groupId>de.codecentric</groupId>-->
|
||||||
<artifactId>spring-boot-admin-starter-server</artifactId>
|
<!-- <artifactId>spring-boot-admin-starter-server</artifactId>-->
|
||||||
<version>${spring-boot-admin.version}</version>
|
<!-- <version>${spring-boot-admin.version}</version>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>de.codecentric</groupId>
|
<!-- <groupId>de.codecentric</groupId>-->
|
||||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
|
||||||
<version>${spring-boot-admin.version}</version>
|
<!-- <version>${spring-boot-admin.version}</version>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
<!--redisson-->
|
<!--redisson-->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -318,6 +318,12 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xmzs</groupId>
|
||||||
|
<artifactId>ruoyi-midjourney</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xmzs</groupId>
|
<groupId>com.xmzs</groupId>
|
||||||
<artifactId>ruoyi-generator</artifactId>
|
<artifactId>ruoyi-generator</artifactId>
|
||||||
|
|||||||
@@ -59,6 +59,11 @@
|
|||||||
<artifactId>ruoyi-job</artifactId>
|
<artifactId>ruoyi-job</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xmzs</groupId>
|
||||||
|
<artifactId>ruoyi-midjourney</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 代码生成-->
|
<!-- 代码生成-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xmzs</groupId>
|
<groupId>com.xmzs</groupId>
|
||||||
@@ -71,10 +76,10 @@
|
|||||||
<artifactId>ruoyi-demo</artifactId>
|
<artifactId>ruoyi-demo</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>de.codecentric</groupId>
|
<!-- <groupId>de.codecentric</groupId>-->
|
||||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class CaptchaController {
|
|||||||
String code = RandomUtil.randomNumbers(4);
|
String code = RandomUtil.randomNumbers(4);
|
||||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
try {
|
try {
|
||||||
MailUtils.sendText(emailRequest.getUsername(), "【GPT助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
MailUtils.sendText(emailRequest.getUsername(), "【熊猫助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("验证码短信发送异常 => {}", e.getMessage());
|
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package com.xmzs.controller;
|
|||||||
|
|
||||||
import com.xmzs.common.chat.domain.request.ChatRequest;
|
import com.xmzs.common.chat.domain.request.ChatRequest;
|
||||||
import com.xmzs.common.chat.domain.request.Dall3Request;
|
import com.xmzs.common.chat.domain.request.Dall3Request;
|
||||||
|
import com.xmzs.common.chat.domain.request.MjTaskRequest;
|
||||||
|
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||||
import com.xmzs.common.chat.entity.images.Item;
|
import com.xmzs.common.chat.entity.images.Item;
|
||||||
|
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||||
import com.xmzs.common.core.domain.R;
|
import com.xmzs.common.core.domain.R;
|
||||||
import com.xmzs.common.core.domain.model.LoginUser;
|
import com.xmzs.common.core.domain.model.LoginUser;
|
||||||
import com.xmzs.common.core.exception.base.BaseException;
|
import com.xmzs.common.core.exception.base.BaseException;
|
||||||
@@ -13,19 +16,30 @@ import com.xmzs.common.satoken.utils.LoginHelper;
|
|||||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||||
import com.xmzs.system.domain.vo.ChatMessageVo;
|
import com.xmzs.system.domain.vo.ChatMessageVo;
|
||||||
import com.xmzs.system.service.IChatMessageService;
|
import com.xmzs.system.service.IChatMessageService;
|
||||||
import com.xmzs.system.service.SseService;
|
import com.xmzs.system.service.ISseService;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.io.InputStreamResource;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 描述:
|
* 描述:
|
||||||
*
|
*
|
||||||
@@ -37,38 +51,58 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ChatController {
|
public class ChatController {
|
||||||
|
|
||||||
private final SseService sseService;
|
private final ISseService ISseService;
|
||||||
|
|
||||||
private final IChatMessageService chatMessageService;
|
private final IChatMessageService chatMessageService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天接口
|
* 聊天接口
|
||||||
*/
|
*/
|
||||||
@PostMapping("/chat")
|
@PostMapping("/chat")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest) {
|
public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest, HttpServletResponse response) {
|
||||||
if("gpt-4-all".equals(chatRequest.getModel())
|
return ISseService.sseChat(chatRequest);
|
||||||
|| chatRequest.getModel().startsWith("gpt-4-gizmo")
|
|
||||||
|| chatRequest.getModel().startsWith("net-")
|
|
||||||
){
|
|
||||||
return sseService.transitChat(chatRequest);
|
|
||||||
}
|
|
||||||
if("azure-gpt-3.5".equals(chatRequest.getModel())){
|
|
||||||
return sseService.azureChat(chatRequest);
|
|
||||||
}
|
|
||||||
return sseService.sseChat(chatRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音转文本
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
@PostMapping("/audio")
|
||||||
|
@ResponseBody
|
||||||
|
public WhisperResponse audio(@RequestParam("file") MultipartFile file) {
|
||||||
|
WhisperResponse whisperResponse = ISseService.speechToTextTranscriptionsV2(file);
|
||||||
|
return whisperResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本转语音
|
||||||
|
*
|
||||||
|
* @param textToSpeech
|
||||||
|
*/
|
||||||
|
@PostMapping("/speech")
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<Resource> speech(@RequestBody TextToSpeech textToSpeech) {
|
||||||
|
return ISseService.textToSpeed(textToSpeech);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/dall3")
|
@PostMapping("/dall3")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public R<List<Item>> dall3(@RequestBody @Valid Dall3Request request) {
|
public R<List<Item>> dall3(@RequestBody @Valid Dall3Request request) {
|
||||||
return R.ok(sseService.dall3(request));
|
return R.ok(ISseService.dall3(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扣除mj绘图费用
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@PostMapping("/mjTask")
|
@PostMapping("/mjTask")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public R<String> mjTask() {
|
public R<String> mjTask(@RequestBody MjTaskRequest mjTaskRequest) {
|
||||||
sseService.mjTask();
|
ISseService.mjTask(mjTaskRequest);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +111,7 @@ public class ChatController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/chatList")
|
@PostMapping("/chatList")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest,@RequestBody PageQuery pageQuery) {
|
public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest, @RequestBody PageQuery pageQuery) {
|
||||||
// 默认查询当前登录用户消息记录
|
// 默认查询当前登录用户消息记录
|
||||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ public class PayController {
|
|||||||
return R.ok(paymentOrdersVo);
|
return R.ok(paymentOrdersVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转通知地址
|
* 跳转通知地址
|
||||||
*
|
*
|
||||||
@@ -138,6 +137,9 @@ public class PayController {
|
|||||||
BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
|
BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
|
||||||
paymentOrdersService.updateByBo(paymentOrdersBo);
|
paymentOrdersService.updateByBo(paymentOrdersBo);
|
||||||
SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
|
SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
|
||||||
|
if(money>9.9){
|
||||||
|
money = money*2;
|
||||||
|
}
|
||||||
sysUserVo.setUserBalance(sysUserVo.getUserBalance()+money);
|
sysUserVo.setUserBalance(sysUserVo.getUserBalance()+money);
|
||||||
SysUserBo sysUserBo = new SysUserBo();
|
SysUserBo sysUserBo = new SysUserBo();
|
||||||
BeanUtil.copyProperties(sysUserVo,sysUserBo);
|
BeanUtil.copyProperties(sysUserVo,sysUserBo);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.xmzs.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
|
import com.xmzs.common.wechat.Wechat;
|
||||||
|
import com.xmzs.system.cofing.KeywordConfig;
|
||||||
|
import com.xmzs.system.cofing.QqConfig;
|
||||||
|
import com.xmzs.system.cofing.WechatConfig;
|
||||||
|
import com.xmzs.system.handler.WechatMessageHandler;
|
||||||
|
import com.xmzs.system.service.ISseService;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 个人微信扩展控制器
|
||||||
|
*
|
||||||
|
* @author WangLe
|
||||||
|
*/
|
||||||
|
@SaIgnore
|
||||||
|
@Slf4j
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
public class WeChatController {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private Wechat wechatBot;
|
||||||
|
|
||||||
|
private final WechatConfig wechatConfig;
|
||||||
|
|
||||||
|
private final ISseService sseService;
|
||||||
|
|
||||||
|
private final KeywordConfig keywordConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信登录二维码
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@PostMapping("/getQr")
|
||||||
|
public void getQr() {
|
||||||
|
//微信
|
||||||
|
if (wechatConfig.getEnable()){
|
||||||
|
log.info("正在登录微信,请按提示操作:");
|
||||||
|
wechatBot = new Wechat(new WechatMessageHandler(sseService, keywordConfig), wechatConfig.getQrPath());
|
||||||
|
wechatBot.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ spring:
|
|||||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||||
url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
username: ry-vue
|
username: ry-vue
|
||||||
password: ry-vue
|
password: xxx
|
||||||
|
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
# slave:
|
# slave:
|
||||||
|
|||||||
@@ -220,11 +220,11 @@ mail:
|
|||||||
# 是否需要用户名密码验证
|
# 是否需要用户名密码验证
|
||||||
auth: true
|
auth: true
|
||||||
# 发送方,遵循RFC-822标准
|
# 发送方,遵循RFC-822标准
|
||||||
from: xxx@163.com
|
from: ageerle@163.com
|
||||||
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
|
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
|
||||||
user: xxx@163.com
|
user: ageerle@163.com
|
||||||
# 密码(填写授权码)
|
# 密码(填写授权码)
|
||||||
pass: pass
|
pass: TOGXBVPYFVPFRQMQ
|
||||||
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
|
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
|
||||||
starttlsEnable: true
|
starttlsEnable: true
|
||||||
# 使用SSL安全连接
|
# 使用SSL安全连接
|
||||||
@@ -318,14 +318,10 @@ websocket:
|
|||||||
path: ''
|
path: ''
|
||||||
# 设置访问源地址
|
# 设置访问源地址
|
||||||
allowedOrigins: '*'
|
allowedOrigins: '*'
|
||||||
# AI助手配置信息
|
# chatgpt配置信息
|
||||||
chat:
|
chat:
|
||||||
apiKey: ''
|
apiKey: 'sk-uMCP3lTg1dQ9L7Xs2bF352Fa216a4c9280577b205dE67e12'
|
||||||
apiHost: ''
|
apiHost: 'https://api.pandarobot.chat/'
|
||||||
# 中转接口
|
|
||||||
transit:
|
|
||||||
apiKey: ''
|
|
||||||
apiHost: 'https://api.gptgod.online/'
|
|
||||||
# 微信小程序配置信息
|
# 微信小程序配置信息
|
||||||
wx:
|
wx:
|
||||||
miniapp:
|
miniapp:
|
||||||
@@ -343,3 +339,37 @@ baidu:
|
|||||||
apiKey: '' # apiKey
|
apiKey: '' # apiKey
|
||||||
secretKey: '' # secretKey
|
secretKey: '' # secretKey
|
||||||
|
|
||||||
|
wechat:
|
||||||
|
# 是否使用微信 true/false
|
||||||
|
enable: true
|
||||||
|
# 生成的登录二维码路径 默认与项目同级
|
||||||
|
qrPath: "./"
|
||||||
|
|
||||||
|
keyword:
|
||||||
|
# 重置会话指令
|
||||||
|
reset: "重置会话"
|
||||||
|
# ai画图指令(DALL·E模型 https://platform.openai.com/docs/models/dall-e)
|
||||||
|
# generation 根据关键词生成图片(https://platform.openai.com/docs/guides/images/generations)
|
||||||
|
image: "ai画图"
|
||||||
|
# ai语音指令(TTS模型 https://platform.openai.com/docs/api-reference/audio)
|
||||||
|
audio: "ai语音"
|
||||||
|
mj:
|
||||||
|
api-secret:
|
||||||
|
task-store:
|
||||||
|
type: in_memory
|
||||||
|
timeout: 30d
|
||||||
|
translate-way: gpt
|
||||||
|
# proxy:
|
||||||
|
# host: 127.0.0.1
|
||||||
|
# port: 10809
|
||||||
|
ng-discord:
|
||||||
|
server: https://discord.pandarobot.chat/
|
||||||
|
cdn: https://app.pandarobot.chat/
|
||||||
|
wss: https://gateway.pandarobot.chat/
|
||||||
|
openai:
|
||||||
|
gpt-api-url: 'https://api.pandarobot.chat/'
|
||||||
|
gpt-api-key: 'sk-xxx'
|
||||||
|
accounts:
|
||||||
|
- guild-id: 'xxxxx'
|
||||||
|
channel-id: 'xxxxx'
|
||||||
|
user-token: 'xx.xx'
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<module>ruoyi-common-tenant</module>
|
<module>ruoyi-common-tenant</module>
|
||||||
<module>ruoyi-common-chat</module>
|
<module>ruoyi-common-chat</module>
|
||||||
<module>ruoyi-common-pay</module>
|
<module>ruoyi-common-pay</module>
|
||||||
|
<module>ruoyi-common-wechat</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<artifactId>ruoyi-common</artifactId>
|
<artifactId>ruoyi-common</artifactId>
|
||||||
|
|||||||
@@ -166,6 +166,13 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 微信模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xmzs</groupId>
|
||||||
|
<artifactId>ruoyi-common-wechat</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 支付模块 -->
|
<!-- 支付模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xmzs</groupId>
|
<groupId>com.xmzs</groupId>
|
||||||
|
|||||||
@@ -70,13 +70,6 @@
|
|||||||
<version>0.5.0</version>
|
<version>0.5.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- azure-ai -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.azure</groupId>
|
|
||||||
<artifactId>azure-ai-openai</artifactId>
|
|
||||||
<version>1.0.0-beta.6</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ public class OpenAIConst {
|
|||||||
|
|
||||||
public final static int SUCCEED_CODE = 200;
|
public final static int SUCCEED_CODE = 200;
|
||||||
|
|
||||||
public final static double GPT3_COST = 0.03;
|
public final static double GPT3_COST = 0.05;
|
||||||
|
|
||||||
public final static double GPT4_COST = 0.3;
|
public final static double GPT4_COST = 0.3;
|
||||||
|
|
||||||
|
public final static double GPT4_ALL_COST = 0.3;
|
||||||
|
|
||||||
/** 绘图费用 */
|
/** 绘图费用 */
|
||||||
public final static double DALL3_COST = 0.3;
|
public final static double DALL3_COST = 0.4;
|
||||||
|
|
||||||
/** 绘图费用-高清 */
|
/** 绘图费用-高清 */
|
||||||
public final static double DALL3_HD_COST = 0.6;
|
public final static double DALL3_HD_COST = 0.8;
|
||||||
|
|
||||||
/** mdjourney绘图费用 */
|
/** mdjourney绘图费用 */
|
||||||
public final static double MJ_COST = 0.3;
|
public final static double MJ_COST = 0.3;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.xmzs.common.chat.domain.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mj任务请求实体类
|
||||||
|
*
|
||||||
|
* @author WangLe
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class MjTaskRequest {
|
||||||
|
|
||||||
|
private String prompt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.xmzs.common.chat.entity.Tts;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TextToSpeech {
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private String model = Model.TTS_1.getName();
|
||||||
|
/**
|
||||||
|
* 音频声音源
|
||||||
|
*
|
||||||
|
* @see TtsVoice
|
||||||
|
*/
|
||||||
|
private String voice;
|
||||||
|
/**
|
||||||
|
* 输入内容
|
||||||
|
*/
|
||||||
|
private String input;
|
||||||
|
/**
|
||||||
|
* 输出音频文件格式
|
||||||
|
*
|
||||||
|
* @see TtsFormat
|
||||||
|
*/
|
||||||
|
@JsonProperty("response_format")
|
||||||
|
private String responseFormat;
|
||||||
|
/**
|
||||||
|
* 速度调节,默认是1,取值范围0.25——4.0
|
||||||
|
*/
|
||||||
|
private Double speed;
|
||||||
|
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum Model {
|
||||||
|
TTS_1("tts-1"),
|
||||||
|
TTS_1_HD("tts-1-hd"),
|
||||||
|
;
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.xmzs.common.chat.entity.Tts;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum TtsFormat {
|
||||||
|
MP3("mp3"),
|
||||||
|
OPUS("opus"),
|
||||||
|
AAC("aac"),
|
||||||
|
FLAC("flac"),
|
||||||
|
;
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.xmzs.common.chat.entity.Tts;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成不同声音的音频
|
||||||
|
* <p>具体语音效果参考:https://platform.openai.com/docs/guides/text-to-speech</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum TtsVoice {
|
||||||
|
|
||||||
|
ALLOY("alloy"),
|
||||||
|
ECHO("echo"),
|
||||||
|
FABLE("fable"),
|
||||||
|
ONYX("onyx"),
|
||||||
|
NOVA("nova"),
|
||||||
|
SHIMMER("shimmer"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
@@ -174,6 +174,10 @@ public class BaseChatCompletion implements Serializable {
|
|||||||
* gpt-3.5-turbo-16k-0613 超长上下文 支持函数
|
* gpt-3.5-turbo-16k-0613 超长上下文 支持函数
|
||||||
*/
|
*/
|
||||||
GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"),
|
GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"),
|
||||||
|
/**
|
||||||
|
* gpt-3.5-turbo-0125 超长上下文 支持函数
|
||||||
|
*/
|
||||||
|
GPT_3_5_TURBO_0125("gpt-3.5-turbo-0125"),
|
||||||
/**
|
/**
|
||||||
* GPT4.0
|
* GPT4.0
|
||||||
*/
|
*/
|
||||||
@@ -209,6 +213,10 @@ public class BaseChatCompletion implements Serializable {
|
|||||||
* 支持图片
|
* 支持图片
|
||||||
*/
|
*/
|
||||||
GPT_4_VISION_PREVIEW("gpt-4-vision-preview"),
|
GPT_4_VISION_PREVIEW("gpt-4-vision-preview"),
|
||||||
|
/**
|
||||||
|
* gpt-4-0613,支持函数
|
||||||
|
*/
|
||||||
|
GPT_4_0125_PREVIEW("gpt-4-0125-preview"),
|
||||||
;
|
;
|
||||||
private final String name;
|
private final String name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.xmzs.common.chat.openai;
|
package com.xmzs.common.chat.openai;
|
||||||
|
|
||||||
|
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||||
import com.xmzs.common.chat.entity.chat.ChatCompletionWithPicture;
|
import com.xmzs.common.chat.entity.chat.ChatCompletionWithPicture;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
@@ -33,6 +34,7 @@ import com.xmzs.common.chat.entity.models.ModelResponse;
|
|||||||
import com.xmzs.common.chat.entity.moderations.Moderation;
|
import com.xmzs.common.chat.entity.moderations.Moderation;
|
||||||
import com.xmzs.common.chat.entity.moderations.ModerationResponse;
|
import com.xmzs.common.chat.entity.moderations.ModerationResponse;
|
||||||
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||||
|
import retrofit2.Call;
|
||||||
import retrofit2.http.*;
|
import retrofit2.http.*;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@@ -340,4 +342,15 @@ public interface OpenAiApi {
|
|||||||
*/
|
*/
|
||||||
@POST("v1/chat/completions")
|
@POST("v1/chat/completions")
|
||||||
Single<ChatCompletionResponse> chatCompletionWithPicture(@Body ChatCompletionWithPicture chatCompletion);
|
Single<ChatCompletionResponse> chatCompletionWithPicture(@Body ChatCompletionWithPicture chatCompletion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本转语音
|
||||||
|
*
|
||||||
|
* @param textToSpeech 参数
|
||||||
|
* @return ResponseBody body
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
@POST("v1/audio/speech")
|
||||||
|
@Streaming
|
||||||
|
Call<ResponseBody> textToSpeech(@Body TextToSpeech textToSpeech);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -668,7 +668,6 @@ public class OpenAiClient {
|
|||||||
return this.chatCompletion(chatCompletion);
|
return this.chatCompletion(chatCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语音翻译:目前仅支持翻译为英文
|
* 语音翻译:目前仅支持翻译为英文
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.hutool.http.ContentType;
|
import cn.hutool.http.ContentType;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.xmzs.common.chat.config.LocalCache;
|
import com.xmzs.common.chat.config.LocalCache;
|
||||||
|
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||||
import com.xmzs.common.chat.entity.billing.BillingUsage;
|
import com.xmzs.common.chat.entity.billing.BillingUsage;
|
||||||
import com.xmzs.common.chat.entity.billing.KeyInfo;
|
import com.xmzs.common.chat.entity.billing.KeyInfo;
|
||||||
import com.xmzs.common.chat.entity.billing.Subscription;
|
import com.xmzs.common.chat.entity.billing.Subscription;
|
||||||
@@ -37,10 +38,12 @@ import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
import retrofit2.Call;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||||
import retrofit2.converter.jackson.JacksonConverterFactory;
|
import retrofit2.converter.jackson.JacksonConverterFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
@@ -344,6 +347,67 @@ public class OpenAiStreamClient {
|
|||||||
Transcriptions transcriptions = Transcriptions.builder().build();
|
Transcriptions transcriptions = Transcriptions.builder().build();
|
||||||
return this.speechToTextTranscriptions(file, transcriptions);
|
return this.speechToTextTranscriptions(file, transcriptions);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 文本转语音(异步)
|
||||||
|
*
|
||||||
|
* @param textToSpeech 参数
|
||||||
|
* @param callback 返回值接收
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public void textToSpeech(TextToSpeech textToSpeech, retrofit2.Callback callback) {
|
||||||
|
retrofit2.Call<ResponseBody> responseBody = this.openAiApi.textToSpeech(textToSpeech);
|
||||||
|
responseBody.enqueue(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本转语音(同步)
|
||||||
|
*
|
||||||
|
* @param textToSpeech 参数
|
||||||
|
* @since 1.1.3
|
||||||
|
*/
|
||||||
|
public ResponseBody textToSpeech(TextToSpeech textToSpeech){
|
||||||
|
Call<ResponseBody> responseBody = this.openAiApi.textToSpeech(textToSpeech);
|
||||||
|
try {
|
||||||
|
return responseBody.execute().body();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BaseException("文本转语音(同步)失败: "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本转语音(克隆)
|
||||||
|
*
|
||||||
|
* @param textToSpeech
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResponseBody textToSpeechClone(TextToSpeech textToSpeech) {
|
||||||
|
String baseUrl = "http://localhost:8081";
|
||||||
|
String spk = "三月七";
|
||||||
|
String text = textToSpeech.getInput();
|
||||||
|
String lang = "zh";
|
||||||
|
|
||||||
|
// 创建OkHttpClient实例
|
||||||
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
|
// 构建请求URL
|
||||||
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
|
||||||
|
urlBuilder.addQueryParameter("spk", spk);
|
||||||
|
urlBuilder.addQueryParameter("text", text);
|
||||||
|
urlBuilder.addQueryParameter("lang", lang);
|
||||||
|
String url = urlBuilder.build().toString();
|
||||||
|
|
||||||
|
// 创建请求对象
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
// 发送请求并处理响应
|
||||||
|
try {
|
||||||
|
return client.newCall(request).execute().body();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BaseException("语音克隆失败!{}",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.knuddels.jtokkit.api.Encoding;
|
|||||||
import com.knuddels.jtokkit.api.EncodingRegistry;
|
import com.knuddels.jtokkit.api.EncodingRegistry;
|
||||||
import com.knuddels.jtokkit.api.EncodingType;
|
import com.knuddels.jtokkit.api.EncodingType;
|
||||||
import com.knuddels.jtokkit.api.ModelType;
|
import com.knuddels.jtokkit.api.ModelType;
|
||||||
|
import com.xmzs.common.chat.entity.chat.BaseChatCompletion;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
||||||
@@ -39,11 +40,13 @@ public class TikTokensUtil {
|
|||||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||||
|
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0125.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_4_32K.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
modelMap.put(ChatCompletion.Model.GPT_4_32K.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_4_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
modelMap.put(ChatCompletion.Model.GPT_4_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_4_32K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
modelMap.put(ChatCompletion.Model.GPT_4_32K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
modelMap.put(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
modelMap.put(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
modelMap.put(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
|
modelMap.put(ChatCompletion.Model.GPT_4_0125_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,28 +10,33 @@ public class PayConfig {
|
|||||||
/**
|
/**
|
||||||
* 商户ID
|
* 商户ID
|
||||||
*/
|
*/
|
||||||
public static String pid = "xx";
|
public static String pid = "xxx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口地址
|
||||||
|
*/
|
||||||
|
public static String payUrl = "https://pay-cloud.vip/mapi.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
public static String key = "xxx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器异步通知地址
|
||||||
|
*/
|
||||||
|
public static String notify_url = "https://www.pandarobot.chat/pay/returnUrl";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面跳转通知地址
|
||||||
|
*/
|
||||||
|
public static String return_url = "https://www.pandarobot.chat/pay/notifyUrl";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付方式
|
* 支付方式
|
||||||
*/
|
*/
|
||||||
public static String type = "wxpay";
|
public static String type = "wxpay";
|
||||||
|
|
||||||
/**
|
|
||||||
* 接口地址
|
|
||||||
*/
|
|
||||||
public static String payUrl = "https://pay.bluetuo.com/mapi.php";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 服务器异步通知地址
|
|
||||||
*/
|
|
||||||
public static String notify_url = "http://xx/pay/returnUrl";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 页面跳转通知地址
|
|
||||||
*/
|
|
||||||
public static String return_url = "http://xx/pay/notifyUrl";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备类型
|
* 设备类型
|
||||||
*/
|
*/
|
||||||
@@ -40,12 +45,6 @@ public class PayConfig {
|
|||||||
/**
|
/**
|
||||||
* 加密方式默认MD5
|
* 加密方式默认MD5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static String sign_type = "MD5";
|
public static String sign_type = "MD5";
|
||||||
|
|
||||||
/**
|
|
||||||
* 私钥
|
|
||||||
*/
|
|
||||||
public static String key = "xx";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,5 +19,4 @@ public interface PayService {
|
|||||||
* @return String
|
* @return String
|
||||||
**/
|
**/
|
||||||
String getPayUrl(String orderNo, String name, double money, String clientIp);
|
String getPayUrl(String orderNo, String name, double money, String clientIp);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,4 @@ public class PayServiceImpl implements PayService {
|
|||||||
JSONObject jsonObject = new JSONObject(body);
|
JSONObject jsonObject = new JSONObject(body);
|
||||||
return (String) jsonObject.get("qrcode");
|
return (String) jsonObject.get("qrcode");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ public class AllUrlHandler implements InitializingBean {
|
|||||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||||
map.keySet().forEach(info -> {
|
map.keySet().forEach(info -> {
|
||||||
// 获取注解上边的 path 替代 path variable 为 *
|
// 获取注解上边的 path 替代 path variable 为 *
|
||||||
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
|
if(info.getPathPatternsCondition()!=null){
|
||||||
|
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
|
||||||
.forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
|
.forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
urls.addAll(set);
|
urls.addAll(set);
|
||||||
}
|
}
|
||||||
|
|||||||
51
ruoyi-common/ruoyi-common-wechat/pom.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.xmzs</groupId>
|
||||||
|
<artifactId>ruoyi-common</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-common-wechat</artifactId>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
ruoyi-common-wechat 微信服务
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- emoji -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.vdurmont</groupId>
|
||||||
|
<artifactId>emoji-java</artifactId>
|
||||||
|
<version>3.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.activation</groupId>
|
||||||
|
<artifactId>activation</artifactId>
|
||||||
|
<version>1.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- qq -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.mamoe</groupId>
|
||||||
|
<artifactId>mirai-core-jvm</artifactId>
|
||||||
|
<version>2.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xmzs</groupId>
|
||||||
|
<artifactId>ruoyi-common-json</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.xmzs.common.wechat;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.controller.LoginController;
|
||||||
|
import com.xmzs.common.wechat.core.MsgCenter;
|
||||||
|
import com.xmzs.common.wechat.face.IMsgHandlerFace;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class Wechat {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Wechat.class);
|
||||||
|
private IMsgHandlerFace msgHandler;
|
||||||
|
|
||||||
|
public Wechat(IMsgHandlerFace msgHandler, String qrPath) {
|
||||||
|
System.setProperty("jsse.enableSNIExtension", "false"); // 防止SSL错误
|
||||||
|
this.msgHandler = msgHandler;
|
||||||
|
|
||||||
|
// 登陆
|
||||||
|
LoginController login = new LoginController();
|
||||||
|
login.login(qrPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
LOG.info("+++++++++++++++++++开始消息处理+++++++++++++++++++++");
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
MsgCenter.handleMsg(msgHandler);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.xmzs.common.wechat.api;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助工具类,该类暂时未用,请忽略
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月22日 下午10:34:46
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AssistTools {
|
||||||
|
private static OkHttpClient client = new OkHttpClient();
|
||||||
|
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
|
||||||
|
|
||||||
|
public static boolean sendQrPicToServer(String username, String password, String uploadUrl, String localPath)
|
||||||
|
throws IOException {
|
||||||
|
File file = new File(localPath);
|
||||||
|
RequestBody requestBody = new MultipartBody.Builder().addFormDataPart("username", username)
|
||||||
|
.addFormDataPart("password", password)
|
||||||
|
.addFormDataPart("file", file.getName(), RequestBody.create(MEDIA_TYPE_PNG, file)).build();
|
||||||
|
Request request = new Request.Builder().url(uploadUrl).post(requestBody).build();
|
||||||
|
Call call = client.newCall(request);
|
||||||
|
try {
|
||||||
|
Response response = call.execute();
|
||||||
|
System.out.println(response.body().string());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,424 @@
|
|||||||
|
package com.xmzs.common.wechat.api;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.activation.MimetypesFileTypeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||||
|
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||||
|
import com.xmzs.common.wechat.beans.RecommendInfo;
|
||||||
|
import com.xmzs.common.wechat.core.Core;
|
||||||
|
import com.xmzs.common.wechat.utils.Config;
|
||||||
|
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.StorageLoginInfoEnum;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.VerifyFriendEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息处理类
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月23日 下午2:30:37
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MessageTools {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(MessageTools.class);
|
||||||
|
private static Core core = Core.getInstance();
|
||||||
|
private static MyHttpClient myHttpClient = core.getMyHttpClient();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据UserName发送文本消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月4日 下午11:17:38
|
||||||
|
* @param msg
|
||||||
|
* @param toUserName
|
||||||
|
*/
|
||||||
|
private static void sendMsg(String text, String toUserName) {
|
||||||
|
if (text == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG.info(String.format("发送消息 %s: %s", toUserName, text));
|
||||||
|
webWxSendMsg(1, text, toUserName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID发送文本消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月6日 上午11:45:51
|
||||||
|
* @param text
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
public static void sendMsgById(String text, String id) {
|
||||||
|
if (text == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendMsg(text, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据NickName发送文本消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月4日 下午11:17:38
|
||||||
|
* @param text
|
||||||
|
* @param nickName
|
||||||
|
*/
|
||||||
|
public static boolean sendMsgByNickName(String text, String nickName) {
|
||||||
|
if (nickName != null) {
|
||||||
|
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||||
|
if (toUserName != null) {
|
||||||
|
webWxSendMsg(1, text, toUserName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月23日 下午2:32:02
|
||||||
|
* @param msgType
|
||||||
|
* @param content
|
||||||
|
* @param toUserName
|
||||||
|
*/
|
||||||
|
public static void webWxSendMsg(int msgType, String content, String toUserName) {
|
||||||
|
String url = String.format(URLEnum.WEB_WX_SEND_MSG.getUrl(), core.getLoginInfo().get("url"));
|
||||||
|
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||||
|
msgMap.put("Type", msgType);
|
||||||
|
msgMap.put("Content", content);
|
||||||
|
msgMap.put("FromUserName", core.getUserName());
|
||||||
|
msgMap.put("ToUserName", toUserName == null ? core.getUserName() : toUserName);
|
||||||
|
msgMap.put("LocalID", new Date().getTime() * 10);
|
||||||
|
msgMap.put("ClientMsgId", new Date().getTime() * 10);
|
||||||
|
Map<String, Object> paramMap = core.getParamMap();
|
||||||
|
paramMap.put("Msg", msgMap);
|
||||||
|
paramMap.put("Scene", 0);
|
||||||
|
try {
|
||||||
|
String paramStr = JSON.toJSONString(paramMap);
|
||||||
|
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||||
|
EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("webWxSendMsg", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传多媒体文件到 微信服务器,目前应该支持3种类型: 1. pic 直接显示,包含图片,表情 2.video 3.doc 显示为文件,包含PDF等
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 上午12:41:13
|
||||||
|
* @param filePath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static JSONObject webWxUploadMedia(String filePath) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (!file.exists() && file.isFile()) {
|
||||||
|
LOG.info("file is not exist");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String url = String.format(URLEnum.WEB_WX_UPLOAD_MEDIA.getUrl(), core.getLoginInfo().get("fileUrl"));
|
||||||
|
String mimeType = new MimetypesFileTypeMap().getContentType(file);
|
||||||
|
String mediaType = "";
|
||||||
|
if (mimeType == null) {
|
||||||
|
mimeType = "text/plain";
|
||||||
|
} else {
|
||||||
|
mediaType = mimeType.split("/")[0].equals("image") ? "pic" : "doc";
|
||||||
|
}
|
||||||
|
String lastModifieDate = new SimpleDateFormat("yyyy MM dd HH:mm:ss").format(new Date());
|
||||||
|
long fileSize = file.length();
|
||||||
|
String passTicket = (String) core.getLoginInfo().get("pass_ticket");
|
||||||
|
String clientMediaId = new Date().getTime()
|
||||||
|
+ String.valueOf(new Random().nextLong()).substring(0, 4);
|
||||||
|
String webwxDataTicket = MyHttpClient.getCookie("webwx_data_ticket");
|
||||||
|
if (webwxDataTicket == null) {
|
||||||
|
LOG.error("get cookie webwx_data_ticket error");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> paramMap = core.getParamMap();
|
||||||
|
|
||||||
|
paramMap.put("ClientMediaId", clientMediaId);
|
||||||
|
paramMap.put("TotalLen", fileSize);
|
||||||
|
paramMap.put("StartPos", 0);
|
||||||
|
paramMap.put("DataLen", fileSize);
|
||||||
|
paramMap.put("MediaType", 4);
|
||||||
|
|
||||||
|
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||||
|
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||||
|
|
||||||
|
builder.addTextBody("id", "WU_FILE_0", ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("name", filePath, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("type", mimeType, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("lastModifieDate", lastModifieDate, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("size", String.valueOf(fileSize), ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("mediatype", mediaType, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("uploadmediarequest", JSON.toJSONString(paramMap), ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("webwx_data_ticket", webwxDataTicket, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addTextBody("pass_ticket", passTicket, ContentType.TEXT_PLAIN);
|
||||||
|
builder.addBinaryBody("filename", file, ContentType.create(mimeType), filePath);
|
||||||
|
HttpEntity reqEntity = builder.build();
|
||||||
|
HttpEntity entity = myHttpClient.doPostFile(url, reqEntity);
|
||||||
|
if (entity != null) {
|
||||||
|
try {
|
||||||
|
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
return JSON.parseObject(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("webWxUploadMedia 错误: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据NickName发送图片消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午10:32:45
|
||||||
|
* @param nackName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean sendPicMsgByNickName(String nickName, String filePath) {
|
||||||
|
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||||
|
if (toUserName != null) {
|
||||||
|
return sendPicMsgByUserId(toUserName, filePath);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户id发送图片消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午10:34:24
|
||||||
|
* @param userId
|
||||||
|
* @param filePath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean sendPicMsgByUserId(String userId, String filePath) {
|
||||||
|
JSONObject responseObj = webWxUploadMedia(filePath);
|
||||||
|
if (responseObj != null) {
|
||||||
|
String mediaId = responseObj.getString("MediaId");
|
||||||
|
if (mediaId != null) {
|
||||||
|
return webWxSendMsgImg(userId, mediaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送图片消息,内部调用
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午10:38:55
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static boolean webWxSendMsgImg(String userId, String mediaId) {
|
||||||
|
String url = String.format("%s/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"),
|
||||||
|
core.getLoginInfo().get("pass_ticket"));
|
||||||
|
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||||
|
msgMap.put("Type", 3);
|
||||||
|
msgMap.put("MediaId", mediaId);
|
||||||
|
msgMap.put("FromUserName", core.getUserSelf().getString("UserName"));
|
||||||
|
msgMap.put("ToUserName", userId);
|
||||||
|
String clientMsgId = String.valueOf(new Date().getTime())
|
||||||
|
+ String.valueOf(new Random().nextLong()).substring(1, 5);
|
||||||
|
msgMap.put("LocalID", clientMsgId);
|
||||||
|
msgMap.put("ClientMsgId", clientMsgId);
|
||||||
|
Map<String, Object> paramMap = core.getParamMap();
|
||||||
|
paramMap.put("BaseRequest", core.getParamMap().get("BaseRequest"));
|
||||||
|
paramMap.put("Msg", msgMap);
|
||||||
|
String paramStr = JSON.toJSONString(paramMap);
|
||||||
|
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||||
|
if (entity != null) {
|
||||||
|
try {
|
||||||
|
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("webWxSendMsgImg 错误: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户id发送文件
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午11:57:36
|
||||||
|
* @param userId
|
||||||
|
* @param filePath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean sendFileMsgByUserId(String userId, String filePath) {
|
||||||
|
String title = new File(filePath).getName();
|
||||||
|
Map<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("appid", Config.API_WXAPPID);
|
||||||
|
data.put("title", title);
|
||||||
|
data.put("totallen", "");
|
||||||
|
data.put("attachid", "");
|
||||||
|
data.put("type", "6"); // APPMSGTYPE_ATTACH
|
||||||
|
data.put("fileext", title.split("\\.")[1]); // 文件后缀
|
||||||
|
JSONObject responseObj = webWxUploadMedia(filePath);
|
||||||
|
if (responseObj != null) {
|
||||||
|
data.put("totallen", responseObj.getString("StartPos"));
|
||||||
|
data.put("attachid", responseObj.getString("MediaId"));
|
||||||
|
} else {
|
||||||
|
LOG.error("sednFileMsgByUserId 错误: ", data);
|
||||||
|
}
|
||||||
|
return webWxSendAppMsg(userId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户昵称发送文件消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月10日 下午10:59:27
|
||||||
|
* @param nickName
|
||||||
|
* @param filePath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean sendFileMsgByNickName(String nickName, String filePath) {
|
||||||
|
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||||
|
if (toUserName != null) {
|
||||||
|
return sendFileMsgByUserId(toUserName, filePath);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部调用
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月10日 上午12:21:28
|
||||||
|
* @param userId
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static boolean webWxSendAppMsg(String userId, Map<String, String> data) {
|
||||||
|
String url = String.format("%s/webwxsendappmsg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"),
|
||||||
|
core.getLoginInfo().get("pass_ticket"));
|
||||||
|
String clientMsgId = String.valueOf(new Date().getTime())
|
||||||
|
+ String.valueOf(new Random().nextLong()).substring(1, 5);
|
||||||
|
String content = "<appmsg appid='wxeb7ec651dd0aefa9' sdkver=''><title>" + data.get("title")
|
||||||
|
+ "</title><des></des><action></action><type>6</type><content></content><url></url><lowurl></lowurl>"
|
||||||
|
+ "<appattach><totallen>" + data.get("totallen") + "</totallen><attachid>" + data.get("attachid")
|
||||||
|
+ "</attachid><fileext>" + data.get("fileext") + "</fileext></appattach><extinfo></extinfo></appmsg>";
|
||||||
|
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||||
|
msgMap.put("Type", data.get("type"));
|
||||||
|
msgMap.put("Content", content);
|
||||||
|
msgMap.put("FromUserName", core.getUserSelf().getString("UserName"));
|
||||||
|
msgMap.put("ToUserName", userId);
|
||||||
|
msgMap.put("LocalID", clientMsgId);
|
||||||
|
msgMap.put("ClientMsgId", clientMsgId);
|
||||||
|
/*
|
||||||
|
* Map<String, Object> paramMap = new HashMap<String, Object>();
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("unchecked") Map<String, Map<String, String>>
|
||||||
|
* baseRequestMap = (Map<String, Map<String, String>>)
|
||||||
|
* core.getLoginInfo() .get("baseRequest"); paramMap.put("BaseRequest",
|
||||||
|
* baseRequestMap.get("BaseRequest"));
|
||||||
|
*/
|
||||||
|
|
||||||
|
Map<String, Object> paramMap = core.getParamMap();
|
||||||
|
paramMap.put("Msg", msgMap);
|
||||||
|
paramMap.put("Scene", 0);
|
||||||
|
String paramStr = JSON.toJSONString(paramMap);
|
||||||
|
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||||
|
if (entity != null) {
|
||||||
|
try {
|
||||||
|
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("错误: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被动添加好友
|
||||||
|
*
|
||||||
|
* @date 2017年6月29日 下午10:08:43
|
||||||
|
* @param msg
|
||||||
|
* @param accept
|
||||||
|
* true 接受 false 拒绝
|
||||||
|
*/
|
||||||
|
public static void addFriend(BaseMsg msg, boolean accept) {
|
||||||
|
if (!accept) { // 不添加
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int status = VerifyFriendEnum.ACCEPT.getCode(); // 接受好友请求
|
||||||
|
RecommendInfo recommendInfo = msg.getRecommendInfo();
|
||||||
|
String userName = recommendInfo.getUserName();
|
||||||
|
String ticket = recommendInfo.getTicket();
|
||||||
|
// 更新好友列表
|
||||||
|
// TODO 此处需要更新好友列表
|
||||||
|
// core.getContactList().add(msg.getJSONObject("RecommendInfo"));
|
||||||
|
|
||||||
|
String url = String.format(URLEnum.WEB_WX_VERIFYUSER.getUrl(), core.getLoginInfo().get("url"),
|
||||||
|
String.valueOf(System.currentTimeMillis() / 3158L), core.getLoginInfo().get("pass_ticket"));
|
||||||
|
|
||||||
|
List<Map<String, Object>> verifyUserList = new ArrayList<Map<String, Object>>();
|
||||||
|
Map<String, Object> verifyUser = new HashMap<String, Object>();
|
||||||
|
verifyUser.put("Value", userName);
|
||||||
|
verifyUser.put("VerifyUserTicket", ticket);
|
||||||
|
verifyUserList.add(verifyUser);
|
||||||
|
|
||||||
|
List<Integer> sceneList = new ArrayList<Integer>();
|
||||||
|
sceneList.add(33);
|
||||||
|
|
||||||
|
JSONObject body = new JSONObject();
|
||||||
|
body.put("BaseRequest", core.getParamMap().get("BaseRequest"));
|
||||||
|
body.put("Opcode", status);
|
||||||
|
body.put("VerifyUserListSize", 1);
|
||||||
|
body.put("VerifyUserList", verifyUserList);
|
||||||
|
body.put("VerifyContent", "");
|
||||||
|
body.put("SceneListCount", 1);
|
||||||
|
body.put("SceneList", sceneList);
|
||||||
|
body.put("skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()));
|
||||||
|
|
||||||
|
String result = null;
|
||||||
|
try {
|
||||||
|
String paramStr = JSON.toJSONString(body);
|
||||||
|
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||||
|
result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("webWxSendMsg", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(result)) {
|
||||||
|
LOG.error("被动添加好友失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package com.xmzs.common.wechat.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.core.Core;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.StorageLoginInfoEnum;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小工具,如获好友列表等
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月4日 下午10:49:16
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class WechatTools {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(WechatTools.class);
|
||||||
|
|
||||||
|
private static Core core = Core.getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名发送文本消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月4日 下午10:43:14
|
||||||
|
* @param msg
|
||||||
|
* @param toUserName
|
||||||
|
*/
|
||||||
|
public static void sendMsgByUserName(String msg, String toUserName) {
|
||||||
|
MessageTools.sendMsgById(msg, toUserName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 通过RealName获取本次UserName
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 如NickName为"yaphone",则获取UserName=
|
||||||
|
* "@1212d3356aea8285e5bbe7b91229936bc183780a8ffa469f2d638bf0d2e4fc63",
|
||||||
|
* 可通过UserName发送消息
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月4日 下午10:56:31
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getUserNameByNickName(String nickName) {
|
||||||
|
for (JSONObject o : core.getContactList()) {
|
||||||
|
if (o.getString("NickName").equals(nickName)) {
|
||||||
|
return o.getString("UserName");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回好友昵称列表
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月4日 下午11:37:20
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<String> getContactNickNameList() {
|
||||||
|
List<String> contactNickNameList = new ArrayList<String>();
|
||||||
|
for (JSONObject o : core.getContactList()) {
|
||||||
|
contactNickNameList.add(o.getString("NickName"));
|
||||||
|
}
|
||||||
|
return contactNickNameList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回好友完整信息列表
|
||||||
|
*
|
||||||
|
* @date 2017年6月26日 下午9:45:39
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<JSONObject> getContactList() {
|
||||||
|
return core.getContactList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回群列表
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月5日 下午9:55:21
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<JSONObject> getGroupList() {
|
||||||
|
return core.getGroupList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群ID列表
|
||||||
|
*
|
||||||
|
* @date 2017年6月21日 下午11:42:56
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<String> getGroupIdList() {
|
||||||
|
return core.getGroupIdList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群NickName列表
|
||||||
|
*
|
||||||
|
* @date 2017年6月21日 下午11:43:38
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<String> getGroupNickNameList() {
|
||||||
|
return core.getGroupNickNameList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据groupIdList返回群成员列表
|
||||||
|
*
|
||||||
|
* @date 2017年6月13日 下午11:12:31
|
||||||
|
* @param groupId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static JSONArray getMemberListByGroupId(String groupId) {
|
||||||
|
return core.getGroupMemeberMap().get(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出微信
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月18日 下午11:56:54
|
||||||
|
*/
|
||||||
|
public static void logout() {
|
||||||
|
webWxLogout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean webWxLogout() {
|
||||||
|
String url = String.format(URLEnum.WEB_WX_LOGOUT.getUrl(),
|
||||||
|
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()));
|
||||||
|
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||||
|
params.add(new BasicNameValuePair("redirect", "1"));
|
||||||
|
params.add(new BasicNameValuePair("type", "1"));
|
||||||
|
params.add(
|
||||||
|
new BasicNameValuePair("skey", (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())));
|
||||||
|
try {
|
||||||
|
HttpEntity entity = core.getMyHttpClient().doGet(url, params, false, null);
|
||||||
|
String text = EntityUtils.toString(entity, Consts.UTF_8); // 无消息
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.debug(e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUserInfo() {
|
||||||
|
for (JSONObject o : core.getContactList()) {
|
||||||
|
core.getUserInfoMap().put(o.getString("NickName"), o);
|
||||||
|
core.getUserInfoMap().put(o.getString("UserName"), o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 根据用户昵称设置备注名称
|
||||||
|
*
|
||||||
|
* @date 2017年5月27日 上午12:21:40
|
||||||
|
* @param userName
|
||||||
|
* @param remName
|
||||||
|
*/
|
||||||
|
public static void remarkNameByNickName(String nickName, String remName) {
|
||||||
|
String url = String.format(URLEnum.WEB_WX_REMARKNAME.getUrl(), core.getLoginInfo().get("url"),
|
||||||
|
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||||
|
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||||
|
Map<String, Object> msgMap_BaseRequest = new HashMap<String, Object>();
|
||||||
|
msgMap.put("CmdId", 2);
|
||||||
|
msgMap.put("RemarkName", remName);
|
||||||
|
msgMap.put("UserName", core.getUserInfoMap().get(nickName).get("UserName"));
|
||||||
|
msgMap_BaseRequest.put("Uin", core.getLoginInfo().get(StorageLoginInfoEnum.wxuin.getKey()));
|
||||||
|
msgMap_BaseRequest.put("Sid", core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()));
|
||||||
|
msgMap_BaseRequest.put("Skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()));
|
||||||
|
msgMap_BaseRequest.put("DeviceID", core.getLoginInfo().get(StorageLoginInfoEnum.deviceid.getKey()));
|
||||||
|
msgMap.put("BaseRequest", msgMap_BaseRequest);
|
||||||
|
try {
|
||||||
|
String paramStr = JSON.toJSONString(msgMap);
|
||||||
|
HttpEntity entity = core.getMyHttpClient().doPost(url, paramStr);
|
||||||
|
// String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||||
|
LOG.info("修改备注" + remName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("remarkNameByUserName", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信在线状态
|
||||||
|
*
|
||||||
|
* @date 2017年6月16日 上午12:47:46
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean getWechatStatus() {
|
||||||
|
return core.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.xmzs.common.wechat.beans;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AppInfo
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年7月3日 下午10:38:14
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AppInfo implements Serializable {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private int type;
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(int type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppId() {
|
||||||
|
return appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppId(String appId) {
|
||||||
|
this.appId = appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
package com.xmzs.common.wechat.beans;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到的微信消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年7月3日 下午10:28:06
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BaseMsg implements Serializable {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private int subMsgType;
|
||||||
|
private int voiceLength;
|
||||||
|
private String fileName;
|
||||||
|
private int imgHeight;
|
||||||
|
private String toUserName;
|
||||||
|
private int hasProductId;
|
||||||
|
private int imgStatus;
|
||||||
|
private String url;
|
||||||
|
private int imgWidth;
|
||||||
|
private int forwardFlag;
|
||||||
|
private int status;
|
||||||
|
private String Ticket;
|
||||||
|
/** 推荐消息报文 **/
|
||||||
|
private RecommendInfo recommendInfo;
|
||||||
|
private long createTime;
|
||||||
|
private String newMsgId;
|
||||||
|
/** 文本消息内容 **/
|
||||||
|
private String text;
|
||||||
|
/** 消息类型 **/
|
||||||
|
private int msgType;
|
||||||
|
/** 是否为群消息 **/
|
||||||
|
private boolean groupMsg;
|
||||||
|
private String msgId;
|
||||||
|
private int statusNotifyCode;
|
||||||
|
private AppInfo appInfo;
|
||||||
|
private int appMsgType;
|
||||||
|
private String Type;
|
||||||
|
private int playLength;
|
||||||
|
private String mediaId;
|
||||||
|
private String content;
|
||||||
|
private String statusNotifyUserName;
|
||||||
|
/** 消息发送者ID **/
|
||||||
|
private String fromUserName;
|
||||||
|
private String oriContent;
|
||||||
|
private String fileSize;
|
||||||
|
|
||||||
|
public int getSubMsgType() {
|
||||||
|
return subMsgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubMsgType(int subMsgType) {
|
||||||
|
this.subMsgType = subMsgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVoiceLength() {
|
||||||
|
return voiceLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVoiceLength(int voiceLength) {
|
||||||
|
this.voiceLength = voiceLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileName(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImgHeight() {
|
||||||
|
return imgHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImgHeight(int imgHeight) {
|
||||||
|
this.imgHeight = imgHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToUserName() {
|
||||||
|
return toUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToUserName(String toUserName) {
|
||||||
|
this.toUserName = toUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHasProductId() {
|
||||||
|
return hasProductId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasProductId(int hasProductId) {
|
||||||
|
this.hasProductId = hasProductId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImgStatus() {
|
||||||
|
return imgStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImgStatus(int imgStatus) {
|
||||||
|
this.imgStatus = imgStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImgWidth() {
|
||||||
|
return imgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImgWidth(int imgWidth) {
|
||||||
|
this.imgWidth = imgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getForwardFlag() {
|
||||||
|
return forwardFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForwardFlag(int forwardFlag) {
|
||||||
|
this.forwardFlag = forwardFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTicket() {
|
||||||
|
return Ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTicket(String ticket) {
|
||||||
|
Ticket = ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecommendInfo getRecommendInfo() {
|
||||||
|
return recommendInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecommendInfo(RecommendInfo recommendInfo) {
|
||||||
|
this.recommendInfo = recommendInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(long createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewMsgId() {
|
||||||
|
return newMsgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNewMsgId(String newMsgId) {
|
||||||
|
this.newMsgId = newMsgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMsgType() {
|
||||||
|
return msgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsgType(int msgType) {
|
||||||
|
this.msgType = msgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGroupMsg() {
|
||||||
|
return groupMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupMsg(boolean groupMsg) {
|
||||||
|
this.groupMsg = groupMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsgId() {
|
||||||
|
return msgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsgId(String msgId) {
|
||||||
|
this.msgId = msgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatusNotifyCode() {
|
||||||
|
return statusNotifyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatusNotifyCode(int statusNotifyCode) {
|
||||||
|
this.statusNotifyCode = statusNotifyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppInfo getAppInfo() {
|
||||||
|
return appInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppInfo(AppInfo appInfo) {
|
||||||
|
this.appInfo = appInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAppMsgType() {
|
||||||
|
return appMsgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppMsgType(int appMsgType) {
|
||||||
|
this.appMsgType = appMsgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPlayLength() {
|
||||||
|
return playLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayLength(int playLength) {
|
||||||
|
this.playLength = playLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMediaId() {
|
||||||
|
return mediaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaId(String mediaId) {
|
||||||
|
this.mediaId = mediaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatusNotifyUserName() {
|
||||||
|
return statusNotifyUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatusNotifyUserName(String statusNotifyUserName) {
|
||||||
|
this.statusNotifyUserName = statusNotifyUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFromUserName() {
|
||||||
|
return fromUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFromUserName(String fromUserName) {
|
||||||
|
this.fromUserName = fromUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriContent() {
|
||||||
|
return oriContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriContent(String oriContent) {
|
||||||
|
this.oriContent = oriContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileSize(String fileSize) {
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package com.xmzs.common.wechat.beans;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RecommendInfo
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年7月3日 下午10:35:14
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RecommendInfo implements Serializable {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String ticket;
|
||||||
|
private String userName;
|
||||||
|
private int sex;
|
||||||
|
private int attrStatus;
|
||||||
|
private String city;
|
||||||
|
private String nickName;
|
||||||
|
private int scene;
|
||||||
|
private String province;
|
||||||
|
private String content;
|
||||||
|
private String alias;
|
||||||
|
private String signature;
|
||||||
|
private int opCode;
|
||||||
|
private int qQNum;
|
||||||
|
private int verifyFlag;
|
||||||
|
|
||||||
|
public String getTicket() {
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTicket(String ticket) {
|
||||||
|
this.ticket = ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSex() {
|
||||||
|
return sex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSex(int sex) {
|
||||||
|
this.sex = sex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAttrStatus() {
|
||||||
|
return attrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttrStatus(int attrStatus) {
|
||||||
|
this.attrStatus = attrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCity() {
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCity(String city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickName() {
|
||||||
|
return nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickName(String nickName) {
|
||||||
|
this.nickName = nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScene() {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScene(int scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvince() {
|
||||||
|
return province;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvince(String province) {
|
||||||
|
this.province = province;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlias() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlias(String alias) {
|
||||||
|
this.alias = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignature(String signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOpCode() {
|
||||||
|
return opCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpCode(int opCode) {
|
||||||
|
this.opCode = opCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getqQNum() {
|
||||||
|
return qQNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setqQNum(int qQNum) {
|
||||||
|
this.qQNum = qQNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVerifyFlag() {
|
||||||
|
return verifyFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVerifyFlag(int verifyFlag) {
|
||||||
|
this.verifyFlag = verifyFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package com.xmzs.common.wechat.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.utils.SleepUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.api.WechatTools;
|
||||||
|
import com.xmzs.common.wechat.core.Core;
|
||||||
|
import com.xmzs.common.wechat.service.ILoginService;
|
||||||
|
import com.xmzs.common.wechat.service.impl.LoginServiceImpl;
|
||||||
|
import com.xmzs.common.wechat.thread.CheckLoginStatusThread;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.utils.tools.CommonTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登陆控制器
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月13日 下午12:56:07
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LoginController {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(LoginController.class);
|
||||||
|
private ILoginService loginService = new LoginServiceImpl();
|
||||||
|
private static Core core = Core.getInstance();
|
||||||
|
|
||||||
|
public void login(String qrPath) {
|
||||||
|
if (core.isAlive()) { // 已登陆
|
||||||
|
LOG.info("itchat4j已登陆");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
for (int count = 0; count < 10; count++) {
|
||||||
|
LOG.info("获取UUID");
|
||||||
|
while (loginService.getUuid() == null) {
|
||||||
|
LOG.info("1. 获取微信UUID");
|
||||||
|
while (loginService.getUuid() == null) {
|
||||||
|
LOG.warn("1.1. 获取微信UUID失败,两秒后重新获取");
|
||||||
|
SleepUtils.sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.info("2. 获取登陆二维码图片");
|
||||||
|
if (loginService.getQR(qrPath)) {
|
||||||
|
break;
|
||||||
|
} else if (count == 10) {
|
||||||
|
LOG.error("2.2. 获取登陆二维码图片失败,系统退出");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.info("3. 请扫描二维码图片,并在手机上确认");
|
||||||
|
if (!core.isAlive()) {
|
||||||
|
loginService.login();
|
||||||
|
core.setAlive(true);
|
||||||
|
LOG.info(("登陆成功"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOG.info("4. 登陆超时,请重新扫描二维码图片");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("5. 登陆成功,微信初始化");
|
||||||
|
if (!loginService.webWxInit()) {
|
||||||
|
LOG.info("6. 微信初始化异常");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("6. 开启微信状态通知");
|
||||||
|
loginService.wxStatusNotify();
|
||||||
|
|
||||||
|
LOG.info("7. 清除。。。。");
|
||||||
|
CommonTools.clearScreen();
|
||||||
|
LOG.info(String.format("欢迎回来, %s", core.getNickName()));
|
||||||
|
|
||||||
|
LOG.info("8. 开始接收消息");
|
||||||
|
loginService.startReceiving();
|
||||||
|
|
||||||
|
LOG.info("9. 获取联系人信息");
|
||||||
|
loginService.webWxGetContact();
|
||||||
|
|
||||||
|
LOG.info("10. 获取群好友及群好友列表");
|
||||||
|
loginService.WebWxBatchGetContact();
|
||||||
|
|
||||||
|
LOG.info("11. 缓存本次登陆好友相关消息");
|
||||||
|
WechatTools.setUserInfo(); // 登陆成功后缓存本次登陆好友相关消息(NickName, UserName)
|
||||||
|
|
||||||
|
LOG.info("12.开启微信状态检测线程");
|
||||||
|
new Thread(new CheckLoginStatusThread()).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
package com.xmzs.common.wechat.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||||
|
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||||
|
import com.xmzs.common.wechat.utils.enums.parameters.BaseParaEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心存储类,全局只保存一份,单例模式
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月23日 下午2:33:56
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Core {
|
||||||
|
|
||||||
|
private static Core instance;
|
||||||
|
|
||||||
|
private Core() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Core getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (Core.class) {
|
||||||
|
instance = new Core();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean alive = false;
|
||||||
|
private int memberCount = 0;
|
||||||
|
|
||||||
|
private String indexUrl;
|
||||||
|
|
||||||
|
private String userName;
|
||||||
|
private String nickName;
|
||||||
|
private List<BaseMsg> msgList = new ArrayList<BaseMsg>();
|
||||||
|
|
||||||
|
private JSONObject userSelf; // 登陆账号自身信息
|
||||||
|
private List<JSONObject> memberList = new ArrayList<JSONObject>(); // 好友+群聊+公众号+特殊账号
|
||||||
|
private List<JSONObject> contactList = new ArrayList<JSONObject>();// 好友
|
||||||
|
private List<JSONObject> groupList = new ArrayList<JSONObject>();; // 群
|
||||||
|
private Map<String, JSONArray> groupMemeberMap = new HashMap<String, JSONArray>(); // 群聊成员字典
|
||||||
|
private List<JSONObject> publicUsersList = new ArrayList<JSONObject>();;// 公众号/服务号
|
||||||
|
private List<JSONObject> specialUsersList = new ArrayList<JSONObject>();;// 特殊账号
|
||||||
|
private List<String> groupIdList = new ArrayList<String>(); // 群ID列表
|
||||||
|
private List<String> groupNickNameList = new ArrayList<String>(); // 群NickName列表
|
||||||
|
|
||||||
|
private Map<String, JSONObject> userInfoMap = new HashMap<String, JSONObject>();
|
||||||
|
|
||||||
|
Map<String, Object> loginInfo = new HashMap<String, Object>();
|
||||||
|
// CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
MyHttpClient myHttpClient = MyHttpClient.getInstance();
|
||||||
|
String uuid = null;
|
||||||
|
|
||||||
|
boolean useHotReload = false;
|
||||||
|
String hotReloadDir = "itchat.pkl";
|
||||||
|
int receivingRetryCount = 5;
|
||||||
|
|
||||||
|
private long lastNormalRetcodeTime; // 最后一次收到正常retcode的时间,秒为单位
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getParamMap() {
|
||||||
|
return new HashMap<String, Object>(1) {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
{
|
||||||
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
|
for (BaseParaEnum baseRequest : BaseParaEnum.values()) {
|
||||||
|
map.put(baseRequest.para(), getLoginInfo().get(baseRequest.value()).toString());
|
||||||
|
}
|
||||||
|
put("BaseRequest", map);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlive() {
|
||||||
|
return alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlive(boolean alive) {
|
||||||
|
this.alive = alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JSONObject> getMemberList() {
|
||||||
|
return memberList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemberList(List<JSONObject> memberList) {
|
||||||
|
this.memberList = memberList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getLoginInfo() {
|
||||||
|
return loginInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginInfo(Map<String, Object> loginInfo) {
|
||||||
|
this.loginInfo = loginInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMemberCount() {
|
||||||
|
return memberCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemberCount(int memberCount) {
|
||||||
|
this.memberCount = memberCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseHotReload() {
|
||||||
|
return useHotReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseHotReload(boolean useHotReload) {
|
||||||
|
this.useHotReload = useHotReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHotReloadDir() {
|
||||||
|
return hotReloadDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHotReloadDir(String hotReloadDir) {
|
||||||
|
this.hotReloadDir = hotReloadDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReceivingRetryCount() {
|
||||||
|
return receivingRetryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceivingRetryCount(int receivingRetryCount) {
|
||||||
|
this.receivingRetryCount = receivingRetryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyHttpClient getMyHttpClient() {
|
||||||
|
return myHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BaseMsg> getMsgList() {
|
||||||
|
return msgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsgList(List<BaseMsg> msgList) {
|
||||||
|
this.msgList = msgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMyHttpClient(MyHttpClient myHttpClient) {
|
||||||
|
this.myHttpClient = myHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGroupIdList() {
|
||||||
|
return groupIdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupIdList(List<String> groupIdList) {
|
||||||
|
this.groupIdList = groupIdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JSONObject> getContactList() {
|
||||||
|
return contactList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContactList(List<JSONObject> contactList) {
|
||||||
|
this.contactList = contactList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JSONObject> getGroupList() {
|
||||||
|
return groupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupList(List<JSONObject> groupList) {
|
||||||
|
this.groupList = groupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JSONObject> getPublicUsersList() {
|
||||||
|
return publicUsersList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicUsersList(List<JSONObject> publicUsersList) {
|
||||||
|
this.publicUsersList = publicUsersList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JSONObject> getSpecialUsersList() {
|
||||||
|
return specialUsersList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecialUsersList(List<JSONObject> specialUsersList) {
|
||||||
|
this.specialUsersList = specialUsersList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickName() {
|
||||||
|
return nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickName(String nickName) {
|
||||||
|
this.nickName = nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject getUserSelf() {
|
||||||
|
return userSelf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserSelf(JSONObject userSelf) {
|
||||||
|
this.userSelf = userSelf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, JSONObject> getUserInfoMap() {
|
||||||
|
return userInfoMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserInfoMap(Map<String, JSONObject> userInfoMap) {
|
||||||
|
this.userInfoMap = userInfoMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized long getLastNormalRetcodeTime() {
|
||||||
|
return lastNormalRetcodeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setLastNormalRetcodeTime(long lastNormalRetcodeTime) {
|
||||||
|
this.lastNormalRetcodeTime = lastNormalRetcodeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGroupNickNameList() {
|
||||||
|
return groupNickNameList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupNickNameList(List<String> groupNickNameList) {
|
||||||
|
this.groupNickNameList = groupNickNameList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, JSONArray> getGroupMemeberMap() {
|
||||||
|
return groupMemeberMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupMemeberMap(Map<String, JSONArray> groupMemeberMap) {
|
||||||
|
this.groupMemeberMap = groupMemeberMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIndexUrl() {
|
||||||
|
return indexUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndexUrl(String indexUrl) {
|
||||||
|
this.indexUrl = indexUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package com.xmzs.common.wechat.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.utils.enums.MsgCodeEnum;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.api.MessageTools;
|
||||||
|
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||||
|
import com.xmzs.common.wechat.face.IMsgHandlerFace;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.utils.enums.MsgTypeEnum;
|
||||||
|
import com.xmzs.common.wechat.utils.tools.CommonTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息处理中心
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月14日 下午12:47:50
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MsgCenter {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(MsgCenter.class);
|
||||||
|
|
||||||
|
private static Core core = Core.getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收消息,放入队列
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月23日 下午2:30:48
|
||||||
|
* @param msgList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static JSONArray produceMsg(JSONArray msgList) {
|
||||||
|
JSONArray result = new JSONArray();
|
||||||
|
for (int i = 0; i < msgList.size(); i++) {
|
||||||
|
JSONObject msg = new JSONObject();
|
||||||
|
JSONObject m = msgList.getJSONObject(i);
|
||||||
|
m.put("groupMsg", false);// 是否是群消息
|
||||||
|
if (m.getString("FromUserName").contains("@@") || m.getString("ToUserName").contains("@@")) { // 群聊消息
|
||||||
|
if (m.getString("FromUserName").contains("@@")
|
||||||
|
&& !core.getGroupIdList().contains(m.getString("FromUserName"))) {
|
||||||
|
core.getGroupIdList().add((m.getString("FromUserName")));
|
||||||
|
} else if (m.getString("ToUserName").contains("@@")
|
||||||
|
&& !core.getGroupIdList().contains(m.getString("ToUserName"))) {
|
||||||
|
core.getGroupIdList().add((m.getString("ToUserName")));
|
||||||
|
}
|
||||||
|
// 群消息与普通消息不同的是在其消息体(Content)中会包含发送者id及":<br/>"消息,这里需要处理一下,去掉多余信息,只保留消息内容
|
||||||
|
if (m.getString("Content").contains("<br/>")) {
|
||||||
|
String content = m.getString("Content").substring(m.getString("Content").indexOf("<br/>") + 5);
|
||||||
|
m.put("Content", content);
|
||||||
|
m.put("groupMsg", true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CommonTools.msgFormatter(m, "Content");
|
||||||
|
}
|
||||||
|
if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_TEXT.getCode())) { // words
|
||||||
|
// 文本消息
|
||||||
|
if (m.getString("Url").length() != 0) {
|
||||||
|
String regEx = "(.+?\\(.+?\\))";
|
||||||
|
Matcher matcher = CommonTools.getMatcher(regEx, m.getString("Content"));
|
||||||
|
String data = "Map";
|
||||||
|
if (matcher.find()) {
|
||||||
|
data = matcher.group(1);
|
||||||
|
}
|
||||||
|
msg.put("Type", "Map");
|
||||||
|
msg.put("Text", data);
|
||||||
|
} else {
|
||||||
|
msg.put("Type", MsgTypeEnum.TEXT.getType());
|
||||||
|
msg.put("Text", m.getString("Content"));
|
||||||
|
}
|
||||||
|
m.put("Type", msg.getString("Type"));
|
||||||
|
m.put("Text", msg.getString("Text"));
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_IMAGE.getCode())
|
||||||
|
|| m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_EMOTICON.getCode())) { // 图片消息
|
||||||
|
m.put("Type", MsgTypeEnum.PIC.getType());
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VOICE.getCode())) { // 语音消息
|
||||||
|
m.put("Type", MsgTypeEnum.VOICE.getType());
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VERIFYMSG.getCode())) {// friends
|
||||||
|
// 好友确认消息
|
||||||
|
// MessageTools.addFriend(core, userName, 3, ticket); // 确认添加好友
|
||||||
|
m.put("Type", MsgTypeEnum.VERIFYMSG.getType());
|
||||||
|
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SHARECARD.getCode())) { // 共享名片
|
||||||
|
m.put("Type", MsgTypeEnum.NAMECARD.getType());
|
||||||
|
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VIDEO.getCode())
|
||||||
|
|| m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MICROVIDEO.getCode())) {// viedo
|
||||||
|
m.put("Type", MsgTypeEnum.VIEDO.getType());
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MEDIA.getCode())) { // 多媒体消息
|
||||||
|
m.put("Type", MsgTypeEnum.MEDIA.getType());
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_STATUSNOTIFY.getCode())) {// phone
|
||||||
|
// init
|
||||||
|
// 微信初始化消息
|
||||||
|
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SYS.getCode())) {// 系统消息
|
||||||
|
m.put("Type", MsgTypeEnum.SYS.getType());
|
||||||
|
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_RECALLED.getCode())) { // 撤回消息
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG.info("Useless msg");
|
||||||
|
}
|
||||||
|
LOG.info("收到消息一条,来自: " + m.getString("FromUserName"));
|
||||||
|
result.add(m);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息处理
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月14日 上午10:52:34
|
||||||
|
* @param msgHandler
|
||||||
|
*/
|
||||||
|
public static void handleMsg(IMsgHandlerFace msgHandler) {
|
||||||
|
while (true) {
|
||||||
|
if (core.getMsgList().size() > 0 && core.getMsgList().get(0).getContent() != null) {
|
||||||
|
if (core.getMsgList().get(0).getContent().length() > 0) {
|
||||||
|
BaseMsg msg = core.getMsgList().get(0);
|
||||||
|
if (msg.getType() != null) {
|
||||||
|
try {
|
||||||
|
if (msg.getType().equals(MsgTypeEnum.TEXT.getType())) {
|
||||||
|
String result = msgHandler.textMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.PIC.getType())) {
|
||||||
|
|
||||||
|
String result = msgHandler.picMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.VOICE.getType())) {
|
||||||
|
String result = msgHandler.voiceMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.VIEDO.getType())) {
|
||||||
|
String result = msgHandler.viedoMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.NAMECARD.getType())) {
|
||||||
|
String result = msgHandler.nameCardMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.SYS.getType())) { // 系统消息
|
||||||
|
msgHandler.sysMsgHandle(msg);
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.VERIFYMSG.getType())) { // 确认添加好友消息
|
||||||
|
String result = msgHandler.verifyAddFriendMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result,
|
||||||
|
core.getMsgList().get(0).getRecommendInfo().getUserName());
|
||||||
|
} else if (msg.getType().equals(MsgTypeEnum.MEDIA.getType())) { // 多媒体消息
|
||||||
|
String result = msgHandler.mediaMsgHandle(msg);
|
||||||
|
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.getMsgList().remove(0);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.xmzs.common.wechat.face;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息处理接口
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月20日 上午12:13:49
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IMsgHandlerFace {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月20日 上午12:15:00
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String textMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理图片消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月21日 下午11:07:06
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String picMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理声音消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月22日 上午12:09:44
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String voiceMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理小视频消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月23日 下午12:19:50
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String viedoMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理名片消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月1日 上午12:50:50
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String nameCardMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理系统消息
|
||||||
|
*
|
||||||
|
* @author Relyn
|
||||||
|
* @date 2017年6月21日17:43:51
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public void sysMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理确认添加好友消息
|
||||||
|
*
|
||||||
|
* @date 2017年6月28日 下午10:15:30
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String verifyAddFriendMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理收到的文件消息
|
||||||
|
*
|
||||||
|
* @date 2017年7月21日 下午11:59:14
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String mediaMsgHandle(BaseMsg msg);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.xmzs.common.wechat.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登陆服务接口
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月13日 上午12:07:21
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ILoginService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登陆
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:14:07
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean login();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取UUID
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:21:40
|
||||||
|
* @param qrPath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getUuid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码图片
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:13:51
|
||||||
|
* @param qrPath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean getQR(String qrPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* web初始化
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:14:13
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean webWxInit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信状态通知
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:14:24
|
||||||
|
*/
|
||||||
|
void wxStatusNotify();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收消息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 上午12:14:37
|
||||||
|
*/
|
||||||
|
void startReceiving();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信联系人
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月13日 下午2:26:18
|
||||||
|
*/
|
||||||
|
void webWxGetContact();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量获取联系人信息
|
||||||
|
*
|
||||||
|
* @date 2017年6月22日 下午11:24:35
|
||||||
|
*/
|
||||||
|
void WebWxBatchGetContact();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.xmzs.common.wechat.thread;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.core.Core;
|
||||||
|
import com.xmzs.common.wechat.utils.SleepUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查微信在线状态
|
||||||
|
* <p>
|
||||||
|
* 如何来感知微信状态?
|
||||||
|
* 微信会有心跳包,LoginServiceImpl.syncCheck()正常在线情况下返回的消息中retcode报文应该为"0",心跳间隔一般在25秒,
|
||||||
|
* 那么可以通过最后收到正常报文的时间来作为判断是否在线的依据。若报文间隔大于60秒,则认为已掉线。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月17日 下午10:53:15
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CheckLoginStatusThread implements Runnable {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(CheckLoginStatusThread.class);
|
||||||
|
private Core core = Core.getInstance();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (core.isAlive()) {
|
||||||
|
long t1 = System.currentTimeMillis(); // 秒为单位
|
||||||
|
if (t1 - core.getLastNormalRetcodeTime() > 60 * 1000) { // 超过60秒,判为离线
|
||||||
|
core.setAlive(false);
|
||||||
|
LOG.info("微信已离线");
|
||||||
|
}
|
||||||
|
SleepUtils.sleep(10 * 1000); // 休眠10秒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.xmzs.common.wechat.utils;
|
||||||
|
|
||||||
|
import com.xmzs.common.wechat.utils.enums.OsNameEnum;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置信息
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月23日 下午2:26:21
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
public static final String API_WXAPPID = "API_WXAPPID";
|
||||||
|
|
||||||
|
public static final String picDir = "D://itchat4j";
|
||||||
|
public static final String VERSION = "1.4.1";
|
||||||
|
public static final String BASE_URL = "https://login.weixin.qq.com";
|
||||||
|
public static final String REFERER = "https://wx.qq.com/?&lang=zh_CN&target=t";
|
||||||
|
public static final String OS = "";
|
||||||
|
public static final String DIR = "";
|
||||||
|
public static final String DEFAULT_QR = "QR.jpg";
|
||||||
|
public static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36";
|
||||||
|
public static final String UOS_PATCH_CLIENT_VERSION = "2.0.0";
|
||||||
|
|
||||||
|
public static final String UOS_PATCH_EXTSPAM =
|
||||||
|
"Go8FCIkFEokFCggwMDAwMDAwMRAGGvAESySibk50w5Wb3uTl2c2h64jVVrV7gNs06GFlWplHQbY/5FfiO++1yH4ykCyNPWKXmco+wfQzK5R98D3so7rJ5LmGFvBLjGceleySrc3SOf2Pc1gVehzJgODeS0lDL3/I/0S2SSE98YgKleq6Uqx6ndTy9yaL9qFxJL7eiA/R3SEfTaW1SBoSITIu+EEkXff+Pv8NHOk7N57rcGk1w0ZzRrQDkXTOXFN2iHYIzAAZPIOY45Lsh+A4slpgnDiaOvRtlQYCt97nmPLuTipOJ8Qc5pM7ZsOsAPPrCQL7nK0I7aPrFDF0q4ziUUKettzW8MrAaiVfmbD1/VkmLNVqqZVvBCtRblXb5FHmtS8FxnqCzYP4WFvz3T0TcrOqwLX1M/DQvcHaGGw0B0y4bZMs7lVScGBFxMj3vbFi2SRKbKhaitxHfYHAOAa0X7/MSS0RNAjdwoyGHeOepXOKY+h3iHeqCvgOH6LOifdHf/1aaZNwSkGotYnYScW8Yx63LnSwba7+hESrtPa/huRmB9KWvMCKbDThL/nne14hnL277EDCSocPu3rOSYjuB9gKSOdVmWsj9Dxb/iZIe+S6AiG29Esm+/eUacSba0k8wn5HhHg9d4tIcixrxveflc8vi2/wNQGVFNsGO6tB5WF0xf/plngOvQ1/ivGV/C1Qpdhzznh0ExAVJ6dwzNg7qIEBaw+BzTJTUuRcPk92Sn6QDn2Pu3mpONaEumacjW4w6ipPnPw+g2TfywJjeEcpSZaP4Q3YV5HG8D6UjWA4GSkBKculWpdCMadx0usMomsSS/74QgpYqcPkmamB4nVv1JxczYITIqItIKjD35IGKAUwAA==";
|
||||||
|
|
||||||
|
|
||||||
|
public static final ArrayList<String> API_SPECIAL_USER = new ArrayList<String>(Arrays.asList("filehelper", "weibo",
|
||||||
|
"qqmail", "fmessage", "tmessage", "qmessage", "qqsync", "floatbottle", "lbsapp", "shakeapp", "medianote",
|
||||||
|
"qqfriend", "readerapp", "blogapp", "facebookapp", "masssendapp", "meishiapp", "feedsapp", "voip",
|
||||||
|
"blogappweixin", "brandsessionholder", "weixin", "weixinreminder", "officialaccounts", "wxitil",
|
||||||
|
"notification_messages", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "userexperience_alarm"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件目录
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月8日 下午10:27:42
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getLocalPath() {
|
||||||
|
String localPath = null;
|
||||||
|
try {
|
||||||
|
localPath = new File("").getCanonicalPath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统平台
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月8日 下午10:27:53
|
||||||
|
*/
|
||||||
|
public static OsNameEnum getOsNameEnum() {
|
||||||
|
String os = System.getProperty("os.name").toUpperCase();
|
||||||
|
if (os.indexOf(OsNameEnum.DARWIN.toString()) >= 0) {
|
||||||
|
return OsNameEnum.DARWIN;
|
||||||
|
} else if (os.indexOf(OsNameEnum.WINDOWS.toString()) >= 0) {
|
||||||
|
return OsNameEnum.WINDOWS;
|
||||||
|
} else if (os.indexOf(OsNameEnum.LINUX.toString()) >= 0) {
|
||||||
|
return OsNameEnum.LINUX;
|
||||||
|
} else if (os.indexOf(OsNameEnum.MAC.toString()) >= 0) {
|
||||||
|
return OsNameEnum.MAC;
|
||||||
|
}
|
||||||
|
return OsNameEnum.OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.xmzs.common.wechat.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常量
|
||||||
|
*
|
||||||
|
* @author https=//github.com/yaphone
|
||||||
|
* @date 创建时间:2017年5月5日 下午11=29=04
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ConstantConfigEnum {
|
||||||
|
public static final int APPMSGTYPE_TEXT = 1;
|
||||||
|
public static final int APPMSGTYPE_IMG = 2;
|
||||||
|
public static final int APPMSGTYPE_AUDIO = 3;
|
||||||
|
public static final int APPMSGTYPE_VIDEO = 4;
|
||||||
|
public static final int APPMSGTYPE_URL = 5;
|
||||||
|
public static final int APPMSGTYPE_ATTACH = 6;
|
||||||
|
public static final int APPMSGTYPE_OPEN = 7;
|
||||||
|
public static final int APPMSGTYPE_EMOJI = 8;
|
||||||
|
public static final int APPMSGTYPE_VOICE_REMIND = 9;
|
||||||
|
public static final int APPMSGTYPE_SCAN_GOOD = 10;
|
||||||
|
public static final int APPMSGTYPE_GOOD = 13;
|
||||||
|
public static final int APPMSGTYPE_EMOTION = 15;
|
||||||
|
public static final int APPMSGTYPE_CARD_TICKET = 16;
|
||||||
|
public static final int APPMSGTYPE_REALTIME_SHARE_LOCATION = 17;
|
||||||
|
// public static final int APPMSGTYPE_TRANSFERS = 2e3;
|
||||||
|
public static final int APPMSGTYPE_RED_ENVELOPES = 2001;
|
||||||
|
public static final int APPMSGTYPE_READER_TYPE = 100001;
|
||||||
|
public static final int UPLOAD_MEDIA_TYPE_IMAGE = 1;
|
||||||
|
public static final int UPLOAD_MEDIA_TYPE_VIDEO = 2;
|
||||||
|
public static final int UPLOAD_MEDIA_TYPE_AUDIO = 3;
|
||||||
|
public static final int UPLOAD_MEDIA_TYPE_ATTACHMENT = 4;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.xmzs.common.wechat.utils;
|
||||||
|
|
||||||
|
public class MsgKeywords {
|
||||||
|
public static String newFriendStr = "我通过了你的朋友验证请求";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package com.xmzs.common.wechat.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.ClientProtocolException;
|
||||||
|
import org.apache.http.client.CookieStore;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.cookie.Cookie;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.BasicCookieStore;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP访问类,对Apache HttpClient进行简单封装,适配器模式
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月9日 下午7:05:04
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MyHttpClient {
|
||||||
|
private Logger logger = Logger.getLogger("MyHttpClient");
|
||||||
|
|
||||||
|
private static CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
|
||||||
|
private static MyHttpClient instance = null;
|
||||||
|
|
||||||
|
private static CookieStore cookieStore;
|
||||||
|
|
||||||
|
static {
|
||||||
|
cookieStore = new BasicCookieStore();
|
||||||
|
|
||||||
|
// 将CookieStore设置到httpClient中
|
||||||
|
httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCookie(String name) {
|
||||||
|
List<Cookie> cookies = cookieStore.getCookies();
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
if (cookie.getName().equalsIgnoreCase(name)) {
|
||||||
|
return cookie.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyHttpClient() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取cookies
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午8:37:17
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static MyHttpClient getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (MyHttpClient.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new MyHttpClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理GET请求
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月9日 下午7:06:19
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpEntity doGet(String url, List<BasicNameValuePair> params, boolean redirect,
|
||||||
|
Map<String, String> headerMap) {
|
||||||
|
HttpEntity entity = null;
|
||||||
|
HttpGet httpGet = new HttpGet();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (params != null) {
|
||||||
|
String paramStr = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8));
|
||||||
|
httpGet = new HttpGet(url + "?" + paramStr);
|
||||||
|
} else {
|
||||||
|
httpGet = new HttpGet(url);
|
||||||
|
}
|
||||||
|
if (!redirect) {
|
||||||
|
httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); // 禁止重定向
|
||||||
|
}
|
||||||
|
httpGet.setHeader("User-Agent", Config.USER_AGENT);
|
||||||
|
httpGet.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||||
|
httpGet.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||||
|
httpGet.setHeader("referer", Config.REFERER);
|
||||||
|
if (headerMap != null) {
|
||||||
|
Set<Entry<String, String>> entries = headerMap.entrySet();
|
||||||
|
for (Entry<String, String> entry : entries) {
|
||||||
|
httpGet.setHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseableHttpResponse response = httpClient.execute(httpGet);
|
||||||
|
entity = response.getEntity();
|
||||||
|
} catch (ClientProtocolException e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理POST请求
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年4月9日 下午7:06:35
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpEntity doPost(String url, String paramsStr) {
|
||||||
|
HttpEntity entity = null;
|
||||||
|
HttpPost httpPost = new HttpPost();
|
||||||
|
try {
|
||||||
|
StringEntity params = new StringEntity(paramsStr, Consts.UTF_8);
|
||||||
|
httpPost = new HttpPost(url);
|
||||||
|
httpPost.setEntity(params);
|
||||||
|
httpPost.setHeader("Content-type", "application/json; charset=utf-8");
|
||||||
|
httpPost.setHeader("User-Agent", Config.USER_AGENT);
|
||||||
|
httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||||
|
httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||||
|
httpPost.setHeader("referer", Config.REFERER);
|
||||||
|
|
||||||
|
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||||
|
entity = response.getEntity();
|
||||||
|
} catch (ClientProtocolException e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到服务器
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 2017年5月7日 下午9:19:23
|
||||||
|
* @param url
|
||||||
|
* @param reqEntity
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpEntity doPostFile(String url, HttpEntity reqEntity) {
|
||||||
|
HttpEntity entity = null;
|
||||||
|
HttpPost httpPost = new HttpPost(url);
|
||||||
|
httpPost.setHeader("User-Agent", Config.USER_AGENT);
|
||||||
|
httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||||
|
httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||||
|
httpPost.setHeader("referer", Config.REFERER);
|
||||||
|
|
||||||
|
httpPost.setEntity(reqEntity);
|
||||||
|
try {
|
||||||
|
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||||
|
entity = response.getEntity();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CloseableHttpClient getHttpClient() {
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.xmzs.common.wechat.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by xiaoxiaomo on 2017/5/6.
|
||||||
|
*/
|
||||||
|
public class SleepUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 毫秒为单位
|
||||||
|
* @param time
|
||||||
|
*/
|
||||||
|
public static void sleep( long time ){
|
||||||
|
try {
|
||||||
|
Thread.sleep( time );
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.xmzs.common.wechat.utils.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型
|
||||||
|
*
|
||||||
|
* @author https://github.com/yaphone
|
||||||
|
* @date 创建时间:2017年4月23日 下午12:15:00
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum MsgCodeEnum {
|
||||||
|
|
||||||
|
// public static final int MSGTYPE_TEXT = 1; // 文本消息类型
|
||||||
|
// public static final int MSGTYPE_IMAGE = 3; // 图片消息
|
||||||
|
// public static final int MSGTYPE_VOICE = 34; // 语音消息
|
||||||
|
// public static final int MSGTYPE_VIDEO = 43; // 小视频消息
|
||||||
|
// public static final int MSGTYPE_MICROVIDEO = 62; // 短视频消息
|
||||||
|
// public static final int MSGTYPE_EMOTICON = 47; // 表情消息
|
||||||
|
// public static final int MSGTYPE_APP = 49;
|
||||||
|
// public static final int MSGTYPE_VOIPMSG = 50;
|
||||||
|
// public static final int MSGTYPE_VOIPNOTIFY = 52;
|
||||||
|
// public static final int MSGTYPE_VOIPINVITE = 53;
|
||||||
|
// public static final int MSGTYPE_LOCATION = 48;
|
||||||
|
// public static final int MSGTYPE_STATUSNOTIFY = 51;
|
||||||
|
// public static final int MSGTYPE_SYSNOTICE = 9999;
|
||||||
|
// public static final int MSGTYPE_POSSIBLEFRIEND_MSG = 40;
|
||||||
|
// public static final int MSGTYPE_VERIFYMSG = 37;
|
||||||
|
// public static final int MSGTYPE_SHARECARD = 42;
|
||||||
|
// public static final int MSGTYPE_SYS = 10000;
|
||||||
|
// public static final int MSGTYPE_RECALLED = 10002;
|
||||||
|
MSGTYPE_TEXT(1, "文本消息类型"),
|
||||||
|
MSGTYPE_IMAGE(3, "图片消息"),
|
||||||
|
MSGTYPE_VOICE(34, "语音消息"),
|
||||||
|
MSGTYPE_VIDEO(43, "小视频消息"),
|
||||||
|
MSGTYPE_MICROVIDEO(62, "短视频消息"),
|
||||||
|
MSGTYPE_EMOTICON(47, "表情消息"),
|
||||||
|
MSGTYPE_MEDIA(49, "多媒体消息"),
|
||||||
|
MSGTYPE_VOIPMSG(50, ""),
|
||||||
|
MSGTYPE_VOIPNOTIFY(52, ""),
|
||||||
|
MSGTYPE_VOIPINVITE(53, ""),
|
||||||
|
MSGTYPE_LOCATION(48, ""),
|
||||||
|
MSGTYPE_STATUSNOTIFY(51, ""),
|
||||||
|
MSGTYPE_SYSNOTICE(9999, ""),
|
||||||
|
MSGTYPE_POSSIBLEFRIEND_MSG(40, ""),
|
||||||
|
MSGTYPE_VERIFYMSG(37, "好友请求"),
|
||||||
|
MSGTYPE_SHARECARD(42, ""),
|
||||||
|
MSGTYPE_SYS(10000, "系统消息"),
|
||||||
|
MSGTYPE_RECALLED(10002, "")
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
MsgCodeEnum(int code, String type) {
|
||||||
|
this.code = code;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||