package com.ylx.web.controller.massage; import cn.hutool.core.io.FileUtil; import cn.hutool.extra.qrcode.QrCodeUtil; import cn.hutool.extra.qrcode.QrConfig; import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ylx.common.annotation.Log; import com.ylx.common.config.RuoYiConfig; import com.ylx.common.constant.Constants; import com.ylx.common.core.controller.BaseController; import com.ylx.common.core.domain.AjaxResult; import com.ylx.common.core.domain.R; import com.ylx.common.core.domain.model.WxLoginUser; import com.ylx.common.core.redis.RedisCache; import com.ylx.common.enums.BusinessType; import com.ylx.common.utils.MessageUtils; import com.ylx.common.utils.StringUtils; import com.ylx.common.utils.file.FileUploadUtils; import com.ylx.framework.config.ServerConfig; import com.ylx.framework.manager.AsyncManager; import com.ylx.framework.manager.factory.AsyncFactory; import com.ylx.framework.web.service.WxTokenService; import com.ylx.massage.domain.CouponReceive; import com.ylx.massage.domain.TWxUser; import com.ylx.massage.service.CouponReceiveService; import com.ylx.massage.service.TWxUserService; import com.ylx.massage.service.TbFileService; import com.ylx.massage.utils.DateTimeUtils; import com.ylx.massage.utils.JsSignUtil; import com.ylx.massage.utils.StringUtilsMassage; import com.ylx.massage.utils.WeChatUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import static com.ylx.massage.utils.OtherUtil.verification; /** * @author b16mt */ @Slf4j @RestController @Api(tags = {"微信公众号"}) @RequestMapping("/weChat") public class WeChatController extends BaseController { private final static String TOKEN = "abcd1234"; private final static String ENCODING = "UTF-8"; private final static String ACCESS_TOKEN = "access_token"; private final static String REFRESH_TOKEN = "refresh_token"; private final static String OPEN_ID = "openid"; /** * 二维码保存路径 */ private String IMG_PATH = "D:\\Users\\"; @Resource private WeChatUtil weChatUtil; @Resource private TWxUserService wxUserService; @Resource(name = "commonAsyncExecutor") private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Autowired private CouponReceiveService couponReceiveService; @Resource private WxTokenService wxTokenService; @Resource private JsSignUtil jsSignUtil; @Autowired private TbFileService tbFileService; @Autowired private RedisCache redisCache; /** * 微信Token验证 * * @param signature 微信加密签名 * @param timestamp 时间戳 * @param nonce 随机数 * @param echostr 随机字符串 * @param response HTTP响应对象 * @throws Exception 如果处理过程中出现错误 */ @GetMapping("/verifyToken") @Log(title = "公众号pverifyToken", businessType = BusinessType.OTHER) public void verifyToken(@RequestParam(value = "signature") String signature, @RequestParam(value = "timestamp") String timestamp, @RequestParam(value = "nonce") String nonce, @RequestParam(value = "echostr") String echostr, HttpServletResponse response) throws Exception { log.info("微信Token验证 入参: signature:{},timestamp:{},nonce:{},echostr:{},", signature, timestamp, nonce, echostr); // 参数排序 String[] params = new String[]{timestamp, nonce, TOKEN}; Arrays.sort(params); // 校验成功则响应 echostr,失败则不响应 if (verification(params, signature) && echostr != null) { response.setCharacterEncoding(ENCODING); response.getWriter().write(echostr); response.getWriter().flush(); response.getWriter().close(); } } @GetMapping("/setMenu") @ApiOperation("设置菜单") @Log(title = "设置菜单", businessType = BusinessType.OTHER) public Map setMenu() { //获取access_token String token = weChatUtil.getToken(); //获取的二维码ticket return weChatUtil.menuUtil(token); } @GetMapping("/getSignature") @ApiOperation("前端获取jssdk签名") @Log(title = "前端获取jssdk签名", businessType = BusinessType.OTHER) public Map getSignature(String url) { //获取access_token String token = weChatUtil.getToken(); //获取jsapi_ticket String jsapiTicket = weChatUtil.getJsapiTicket(token); //生成签名 return jsSignUtil.sign(url,jsapiTicket); } /** * 处理微信公众号请求信息 * * @param request * @return */ @RequestMapping("/verifyToken") @ResponseBody @Log(title = "处理微信公众号请求信息", businessType = BusinessType.OTHER) public String handlePublicMsg(HttpServletRequest request) throws Exception { log.info("处理微信公众号请求信息:{}", request.toString()); // 获得微信端返回的xml数据 InputStream is = null; InputStreamReader isr = null; BufferedReader br = null; try { is = request.getInputStream(); isr = new InputStreamReader(is, "utf-8"); br = new BufferedReader(isr); String str = null; StringBuffer returnXml = new StringBuffer(); while ((str = br.readLine()) != null) { //返回的是xml数据 returnXml.append(str); } log.info("微信端返回的xml数据:{}", returnXml); Map encryptMap = WeChatUtil.xmlToMap(returnXml.toString()); // 得到公众号传来的加密信息并解密,得到的是明文xml数据 // String decryptXml = WXPublicUtils.decrypt(encryptMap.get("Encrypt")); // 将xml数据转换为map // Map decryptMap = WeChatUtil.xmlToMap(decryptXml); // 区分消息类型 String msgType = encryptMap.get("MsgType"); // 普通消息 if ("text".equals(msgType)) { // 文本消息 // todo 处理文本消息 } else if ("image".equals(msgType)) { // 图片消息 // todo 处理图片消息 } else if ("voice".equals(msgType)) { //语音消息 // todo 处理语音消息 } else if ("video".equals(msgType)) { // 视频消息 // todo 处理视频消息 } else if ("shortvideo".equals(msgType)) { // 小视频消息 // todo 处理小视频消息 } else if ("location".equals(msgType)) { // 地理位置消息 // todo 处理地理位置消息 } else if ("link".equals(msgType)) { // 链接消息 // todo 处理链接消息 } // 事件推送 else if ("event".equals(msgType)) { // 事件消息 // 区分事件推送 String event = encryptMap.get("Event"); if ("subscribe".equals(event)) { // 订阅事件 或 未关注扫描二维码事件 return getString(encryptMap); } else if ("unsubscribe".equals(event)) { // 取消订阅事件 // todo 处理取消订阅事件 } else if ("SCAN".equals(event)) { // 已关注扫描二维码事件 return getString(encryptMap); } else if ("LOCATION".equals(event)) { // 上报地理位置事件 // todo 处理上报地理位置事件 } else if ("CLICK".equals(event)) { // 点击菜单拉取消息时的事件推送事件 // todo 处理点击菜单拉取消息时的事件推送事件 } else if ("VIEW".equals(event)) { // 点击菜单跳转链接时的事件推送 // todo 处理点击菜单跳转链接时的事件推送 } } } catch (Exception e) { logger.error("处理微信公众号请求信息,失败", e); } finally { if (null != is) { is.close(); } if (null != isr) { isr.close(); } if (null != br) { br.close(); } } return null; } private String getString(Map encryptMap) throws Exception { // 返回消息时ToUserName的值与FromUserName的互换 Map returnMap = new HashMap<>(); //添加新用户 TWxUser fromUserName = wxUserService.getByOpenId(encryptMap.get("FromUserName")); if (fromUserName == null) { fromUserName = new TWxUser(); } fromUserName.setcOpenid(encryptMap.get("FromUserName")); fromUserName.setcUpUser(StringUtilsMassage.afterString(encryptMap.get("EventKey"), "qrscene_")); wxUserService.saveOrUpdate(fromUserName); returnMap.put("ToUserName", encryptMap.get("FromUserName")); returnMap.put("FromUserName", encryptMap.get("ToUserName")); returnMap.put("CreateTime", new Date().getTime() + ""); returnMap.put("MsgType", "text"); returnMap.put("Content", "欢迎来到舒压乐园"); String encryptMsg = weChatUtil.mapToXml(returnMap).toString(); return encryptMsg; } /** * 获取微信code * * @param state 状态参数 */ @ApiOperation("获取微信code") @Log(title = "获取微信code", businessType = BusinessType.OTHER) @GetMapping("/getCode") public String weiXinLogin(String state) { // QrConfig config = new QrConfig(300, 300); // 设置边距,即二维码和背景之间的边距 // config.setMargin(1); // 生成二维码到文件,也可以到流 String code = weChatUtil.getCode(state); redisCache.setCacheObject("code", state, 10, TimeUnit.MINUTES); log.info("code:{}", code); // QrCodeUtil.generate(code, config, // FileUtil.file(IMG_PATH)); return code; } /** * 获取token和userInfo * * @param code 微信授权码 * @return 访问令牌 */ @GetMapping("/getAccessToken") @ApiOperation("公众号网页登录") @Log(title = "公众号网页登录", businessType = BusinessType.OTHER) public R getAccessToken(@RequestParam String code) { // 发送get请求获取 AccessToken Map result = weChatUtil.getAccessToken(code); String accessToken = result.get(ACCESS_TOKEN).toString(); String refreshToken = result.get(REFRESH_TOKEN).toString(); String openid = result.get(OPEN_ID).toString(); // 如果用户是第一次进行微信公众号授权 // 进行这一步时用户应点击了同意授权按钮 String userInfoJsom = weChatUtil.getUserInfo(accessToken, openid); // 解析JSON数据 JSONObject jsonObject = new JSONObject(userInfoJsom); log.info("公众号网页登录,{}",jsonObject); // 将用户信息保存到数据库中 LambdaQueryWrapper objectLambdaQueryWrapper = new LambdaQueryWrapper<>(); objectLambdaQueryWrapper.eq(TWxUser::getcOpenid, openid); TWxUser user = wxUserService.getOne(objectLambdaQueryWrapper); if (user == null || StringUtils.isEmpty(user.getcNickName())) { if(user == null){ user = new TWxUser(); user.setcOpenid(openid); TWxUser finalUser = user; //异步 添加新人优惠卷 // threadPoolTaskExecutor.submit(() -> couponReceiveService.submit(new CouponReceive().setOpenid(finalUser.getcOpenid()).setCouponId("1"))); } user.setcOpenid(openid); user.setcNickName(jsonObject.get("nickname").toString()); user.setcIcon(jsonObject.get("headimgurl").toString()); user.setcSessionKey(refreshToken); // user.setcPhone(phoneNumber); wxUserService.saveOrUpdate(user); user.setId(user.getId()); } WxLoginUser wxUser = new WxLoginUser(); BeanUtils.copyProperties(user, wxUser); // 生成并返回令牌 String token = wxTokenService.createToken(wxUser); if (token == null || token.isEmpty()) { return R.fail("生成令牌失败"); } wxUser.setToken(token); // 返回用户信息 // 记录登录信息 AsyncManager.me().execute(AsyncFactory.recordLogininfor(wxUser.getCOpenid(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); return R.ok(wxUser); } /** * 刷新token,微信提供的token是有限时间的,但是对于财务报销系统仅需授权一次的情况下一般不需要进行更新 * * @return accessToken */ @GetMapping("/refreshToken") public String refreshToken() { WxLoginUser wxLoginUser = this.getWxLoginUser(); TWxUser user = wxUserService.getByOpenId(wxLoginUser.getCOpenid()); if (user == null) { throw new RuntimeException("用户不存在"); } // 发送get请求获取 RefreshToken Map result = weChatUtil.refreshToken(user.getcSessionKey()); String accessToken = result.get(ACCESS_TOKEN).toString(); String refreshToken = result.get(REFRESH_TOKEN).toString(); // 更新用户信息 user.setcSessionKey(refreshToken); // 存储数据库 wxUserService.updateById(user); return accessToken; } @ApiOperation("获取公众号二维码") @RequestMapping(value = "getwxQrCode", method = RequestMethod.GET) public Map getWxQrCodeUtil(@RequestParam String openId) { //获取access_token String token = weChatUtil.getToken(); //获取的二维码ticket return weChatUtil.getTicket(token, openId); } @ApiOperation("获取公众号网页二维码") @GetMapping("/getweQrCode") public AjaxResult weiXinLogin1(String openId) { QrConfig config = new QrConfig(300, 300); // 设置边距,即二维码和背景之间的边距 config.setMargin(1); // 生成二维码到文件,也可以到流 String code = "https://www.baidu.com?openId=" + openId; log.info("code:{}", code); String str = IMG_PATH; File generate = QrCodeUtil.generate(code, config, FileUtil.file(RuoYiConfig.getUploadPath() + "/code.png")); MultipartFile multipartFile = FileUploadUtils.getMultipartFile(generate); return tbFileService.uploadFile(multipartFile); } }