开通内网穿透的服务(后端8333,前端8080):
启动内网穿透服务:
创建CourseApiController来实现关键词查询课程信息:
package com.lxl.ggkt.vod.api;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lxl.ggkt.model.vod.Course;
import com.lxl.ggkt.result.Result;
import com.lxl.ggkt.vo.vod.CourseQueryVo;
import com.lxl.ggkt.vo.vod.CourseVo;
import com.lxl.ggkt.vod.service.CourseService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.management.Query;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping(value = "/api/vod/course")
public class CourseApiController {
@Autowired
private CourseService courseService;
//根据课程id查询课程信息
@ApiOperation("根据ID查询课程")
@GetMapping("inner/getById/{courseId}")
public Course getById(
@ApiParam(value = "课程ID", required = true)
@PathVariable Long courseId){
Course course = courseService.getById(courseId);
return course;
}
//根据课程分类查询课程列表(分页)
@ApiOperation("根据课程分类查询课程列表")
@GetMapping("{subjectParentId}/{page}/{limit}")
public Result findPageCourse(@ApiParam(value = "课程一级分类ID", required = true) @PathVariable Long subjectParentId,
@ApiParam(name = "page", value = "当前页码", required = true) @PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true) @PathVariable Long limit){
//封装条件
CourseQueryVo courseQueryVo = new CourseQueryVo();
courseQueryVo.setSubjectParentId(subjectParentId);
//创建page对象
Page<Course> pageParam = new Page<>(page, limit);
Map<String,Object> map = courseService.findPage(pageParam,courseQueryVo);
return Result.ok(map);
}
//根据课程id查询课程详情
@ApiOperation("根据课程id查询课程详情")
@GetMapping("getInfo/{courseId}")
public Result getInfo(@PathVariable Long courseId){
Map<String,Object> map = courseService.getInfoById(courseId);
return Result.ok(map);
}
@ApiOperation("根据关键字查询课程")
@GetMapping("inner/findByKeyword/{keyword}")
public List<Course> findByKeyword(
@ApiParam(value = "关键词",required = true)
@PathVariable String keyword){
QueryWrapper<Course> wrapper = new QueryWrapper<>();
wrapper.like("title",keyword);
List<Course> list = courseService.list(wrapper);
return list;
}
}
公众号消息接收和相关业务模块设计:
MessageController:
package com.lxl.ggkt.wechat.controller;
import com.alibaba.fastjson.JSONObject;
import com.lxl.ggkt.result.Result;
import com.lxl.ggkt.wechat.service.MessageService;
import com.lxl.ggkt.wechat.utils.SHA1;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/wechat/message")
public class MessageController {
private static final String token = "ggkt";
@Autowired
private MessageService messageService;
@GetMapping("/pushPayMessage")
public Result pushPayMessage() throws WxErrorException {
messageService.pushPayMessage(1L);
return Result.ok(null);
}
/**
* 服务器有效性验证
* @param request
* @return
*/
@GetMapping
public String verifyToken(HttpServletRequest request) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
//log.info("signature: {} nonce: {} echostr: {} timestamp: {}", signature, nonce, echostr, timestamp);
if (this.checkSignature(signature, timestamp, nonce)) {
// log.info("token ok");
return echostr;
}
return echostr;
}
private boolean checkSignature(String signature, String timestamp, String nonce) {
String[] str = new String[]{token, timestamp, nonce};
//排序
Arrays.sort(str);
//拼接字符串
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < str.length; i++) {
buffer.append(str[i]);
}
//进行sha1加密
String temp = SHA1.encode(buffer.toString());
//与微信提供的signature进行匹对
return signature.equals(temp);
}
/**
* 接收微信服务器发送来的消息
* @param request
* @return
* @throws Exception
*/
@PostMapping
public String receiveMessage(HttpServletRequest request) throws Exception {
// WxMpXmlMessage wxMpXmlMessage = WxMpXmlMessage.fromXml(request.getInputStream());
// System.out.println(JSONObject.toJSONString(wxMpXmlMessage));
Map<String, String> param = this.parseXml(request);
String message = messageService.receiveMessage(param);
return message;
}
private Map<String, String> parseXml(HttpServletRequest request) throws Exception {
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
inputStream.close();
inputStream = null;
return map;
}
}
MessageServiceImpl:
package com.lxl.ggkt.wechat.service.impl;
import com.lxl.ggkt.client.course.CourseFeignClient;
import com.lxl.ggkt.model.vod.Course;
import com.lxl.ggkt.wechat.service.MessageService;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private CourseFeignClient courseFeignClient;
@Autowired
private WxMpService wxMpService;
//接收消息
@Override
public String receiveMessage(Map<String, String> param) {
String content = "";
try {
String msgType = param.get("MsgType");
switch(msgType){
case "text" :
content = this.search(param);
break;
case "event" :
String event = param.get("Event");
String eventKey = param.get("EventKey");
if("subscribe".equals(event)) {//关注公众号
content = this.subscribe(param);
} else if("unsubscribe".equals(event)) {//取消关注公众号
content = this.unsubscribe(param);
} else if("CLICK".equals(event) && "aboutUs".equals(eventKey)){
content = this.aboutUs(param);
} else {
content = "success";
}
break;
default:
content = "success";
}
} catch (Exception e) {
e.printStackTrace();
content = this.text(param, "请重新输入关键字,没有匹配到相关视频课程").toString();
}
return content;
}
@Override
public void pushPayMessage(long id) {
String openid = "oYtdy6gjiie2TF8o74YJbeJItpCA";
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(openid)//要推送的用户openid
.templateId("ivOxy9PfqDONOWOqOqjvppO-YYebxVbPLBog1e-4nEU")//模板id
.url("http://ggkt2.vipgz1.91tunnel.com/#/pay/"+id)//点击模板消息要访问的网址
.build();
//3,如果是正式版发送消息,,这里需要配置你的信息
templateMessage.addData(new WxMpTemplateData("first", "亲爱的用户:您有一笔订单支付成功。", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword1", "1314520", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword2", "java基础课程", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword3", "2022-01-11", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword4", "100", "#272727"));
templateMessage.addData(new WxMpTemplateData("remark", "感谢你购买课程,如有疑问,随时咨询!", "#272727"));
try {
String msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
}
/**
* 关于我们
* @param param
* @return
*/
private String aboutUs(Map<String, String> param) {
return this.text(param, "硅谷课堂现开设Java、HTML5前端+全栈、大数据、全链路UI/UE设计、人工智能、大数据运维+Python自动化、Android+HTML5混合开发等多门课程;同时,通过视频分享、谷粒学苑在线课堂、大厂学苑直播课堂等多种方式,满足了全国编程爱好者对多样化学习场景的需求,已经为行业输送了大量IT技术人才。").toString();
}
/**
* 处理关注事件
* @param param
* @return
*/
private String subscribe(Map<String, String> param) {
//处理业务
return this.text(param, "感谢你关注“硅谷课堂”,可以根据关键字搜索您想看的视频教程,如:JAVA基础、Spring boot、大数据等").toString();
}
/**
* 处理取消关注事件
* @param param
* @return
*/
private String unsubscribe(Map<String, String> param) {
//处理业务
return "success";
}
/**
* 处理关键字搜索事件
* 图文消息个数;当用户发送文本、图片、语音、视频、图文、地理位置这六种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
* @param param
* @return
*/
private String search(Map<String, String> param) {
String fromusername = param.get("FromUserName");
String tousername = param.get("ToUserName");
String content = param.get("Content");
//单位为秒,不是毫秒
Long createTime = new Date().getTime() / 1000;
StringBuffer text = new StringBuffer();
List<Course> courseList = courseFeignClient.findByKeyword(content);
if(CollectionUtils.isEmpty(courseList)) {
text = this.text(param, "请重新输入关键字,没有匹配到相关视频课程");
} else {
//一次只能返回一个
Random random = new Random();
int num = random.nextInt(courseList.size());
Course course = courseList.get(num);
StringBuffer articles = new StringBuffer();
articles.append("<item>");
articles.append("<Title><![CDATA["+course.getTitle()+"]]></Title>");
articles.append("<Description><![CDATA["+course.getTitle()+"]]></Description>");
articles.append("<PicUrl><![CDATA["+course.getCover()+"]]></PicUrl>");
articles.append("<Url><![CDATA[http://glkt.atguigu.cn/#/liveInfo/"+course.getId()+"]]></Url>");
articles.append("</item>");
text.append("<xml>");
text.append("<ToUserName><![CDATA["+fromusername+"]]></ToUserName>");
text.append("<FromUserName><![CDATA["+tousername+"]]></FromUserName>");
text.append("<CreateTime><![CDATA["+createTime+"]]></CreateTime>");
text.append("<MsgType><![CDATA[news]]></MsgType>");
text.append("<ArticleCount><![CDATA[1]]></ArticleCount>");
text.append("<Articles>");
text.append(articles);
text.append("</Articles>");
text.append("</xml>");
}
return text.toString();
}
/**
* 回复文本
* @param param
* @param content
* @return
*/
private StringBuffer text(Map<String, String> param, String content) {
String fromusername = param.get("FromUserName");
String tousername = param.get("ToUserName");
//单位为秒,不是毫秒
Long createTime = new Date().getTime() / 1000;
StringBuffer text = new StringBuffer();
text.append("<xml>");
text.append("<ToUserName><![CDATA["+fromusername+"]]></ToUserName>");
text.append("<FromUserName><![CDATA["+tousername+"]]></FromUserName>");
text.append("<CreateTime><![CDATA["+createTime+"]]></CreateTime>");
text.append("<MsgType><![CDATA[text]]></MsgType>");
text.append("<Content><![CDATA["+content+"]]></Content>");
text.append("</xml>");
return text;
}
}
测试号申请模版消息展示:
微信授权登录:
测试号配置网页授权获取用户基本信息:
完成前端之后引入微信工具包:
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
</dependencies>
同时配置微信公众号的id和密钥
wechat.mpAppId: wxd4e0bcc0aa6b50d0
wechat.mpAppSecret: ad5712c888df46205b11fb77cddd0f0a
wechat.userInfoUrl: http://ggktlxl.free.idcfengye.com/api/user/wechat/userInfo
工具类ConstantPropertiesUtil和WeChatMpConfig配置见前章。
使用token实现用户信息传递:
导入jwt的依赖:
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies>
增加JWT工具类:
package com.lxl.ggkt.utils;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
public class JwtHelper {
//token字符串有效时间
private static long tokenExpiration = 24*60*60*1000;
//加密编码秘钥
private static String tokenSignKey = "123456";
//根据userid 和 username 生成token字符串
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
//设置token分类
.setSubject("GGKT-USER")
//token字符串有效时长
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
//私有部分(用户信息)
.claim("userId", userId)
.claim("userName", userName)
//根据秘钥使用加密编码方式进行加密,对字符串压缩
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从token字符串获取userid
public static Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
//从token字符串获取getUserName
public static String getUserName(String token) {
if(StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("userName");
}
public static void main(String[] args) {
String token = JwtHelper.createToken(1L, "lucy");
System.out.println(token);
System.out.println(JwtHelper.getUserId(token));
System.out.println(JwtHelper.getUserName(token));
}
}