编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

#微信开发#一个有意思的需求分享+代码讲解

wxchong 2024-07-08 23:33:15 开源技术 9 ℃ 0 评论


涉及的知识点:接收微信消息事件、生成唯一二维码(产品地址+埋码)、图片上传sftp、两张图片的合并、图片上传微信服务、推送客服消息、记录埋码信息等等。

一、前言:

事情是这样的: 前段时间,周五下午,我正在幻想美好的周末的时候,突然收到我们业务发送的一条微信消息,有个需求需要沟通下。

需求是这样的:业务部门要做一个活动,用来推广一款年金产品。

活动的具体要求是这样的:公司员工在公众号输入的关键字,生成一个唯一带有二维码的推广图片(推广图片+二维码)。客户扫描二维码跳转到产品页面,出单以后要定位是谁推广的。

活动的上线时间:周一上午。还能怎么办?周末加班的命~。心里默念“加班使我快乐”“我爱加班”。


二、需求分析

明白了业务要做什么,接下来就是怎么实现的问题了。需求主要有如下几个问题:

1,我怎么可以定为是哪个员工发送的消息呢?

用户在微信后台发送一条消息,在后台,我可以知道用户发送了什么,也可以知道用户的唯一openid,但是openid和员工并没有关联关系。

当时想的是,做一个绑定关系页面,让每一个想推广的用户先把个人信息和微信进行绑定,这样我就有了openid和用户的绑定关系。当然对于用户体验不好。

第二种方案就是通过关键字来定义。

2,关键字怎么定义呢?

获取到用户发送的消息以后,要判断出,用户是谁?用户发送消息是要干嘛?不能用户随便发送一个消息我都给推送一个图片。

当时想的是,活动名称+手机号,因为手机号是唯一的。业务部门需要有员工和手机号的数据。

最后业务确定用活动名称+姓名,确定公司暂时没有重名的员工。

三、具体实现:

1、获取微信推送的消息事件,在后台做分析。

前提条件是微信必须是开发者模式,需要在微信后台配置回到接口地址



2、接收微信推送的消息事件

微信通过post方式推送报文格式如下:


<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
</xml>

报文中Content就是用户发送的消息,如果用户发送的是“某某活动+用户名”,我们就处理这条消息;如果不是,我们可以做固定字段回复,或者转的客服平台,或者对接智能机器人也可以。

处理用用户消息的时候,需要识别是这次活动,还要考虑扩展性,后期同样类型的活动就不需要开发了,数据库配置下就可以实现。

源码如下:


//获取请求参数
 Map<String, String> map = WeChatResplyUtil.parseXmlToMap(request.getInputStream());
 logger.info("微信请求的数据:"+JSONObject.fromObject(map));
 String msgType = map.get("MsgType");
 String userName = map.get("ToUserName");//公众号
 String fromUserName = map.get("FromUserName");//用户openid
 //文本消息类型
 if(WeChatResplyUtil.MESSAGE_TEXT.equals(msgType)){
 //获取用户回复的文本消息
 String content = map.get("Content");
 //判断
 if(StringUtils.isNotBlank(content)&&content.contains("+")){
 //获取配置信息,配置活动关键字,活动图片地址,产品地址
 List<NsType> nsType = nsTypeDao.findByTypeSortAndLikeTypeCode("PRODUCT_URL_QR", keyWord.split("\\+")[0]);
 if(nsType!=null||nsType.size()>=0){
 //多线程处理业务
 threadpool.execute(....);
 return "";
 } 
 }
 //如果不是活动,可以处理关键字回复,人工客服,智能机器人等
 

微信有个要求,获取到用户消息以后,必须5s内响应。确定是活动的关键字,直接响应微信""或者"success"。开启多线程处理业务。

2、根据用户发送的消息生成唯一的二维码,并上传服务器。

获取活动的产品地址productUrl 和唯一的标识randomString 生成二维码(不是微信接口生成的二维码),并上传服务器。然后活动图片和二维码合并。


//获取产品地址
 String productUrl = nsType.get(0).getRelationtypecode();
 //获取推广图片
 String tgImageUrl = nsType.get(0).getTypeDesc();
 //获取随机字符串
 String randomString = "rh_ryb_"+UUID.randomUUID();
 //图片上传途径(别的需求配置过的)
 String filePath = sysConfigService.getConfig("IDNO_IMAGE_URL") + "QRCode/";
 File file = new File(filePath);
 if (!file.exists()) {
 file.mkdirs();
 }
 //生成二维码
 String qrUrl = filePath+randomString+".png";
 boolean qrStatus = QRCodeUtil.generateQRCodeImage(productUrl+randomString, 350, 350, qrUrl);
 if(qrStatus){
 WXControllerLog.INFO("生成二维码成功,二维码地址qrUrl:"+qrUrl);
 }
 //合并图片
 BufferedImage b = ImageUtil.loadImageLocal(qrUrl);
 BufferedImage d = ImageUtil.loadImageLocal(tgImageUrl);
 String img_suc = filePath+randomString+"_suc.jpg";
 ImageUtil.writeImageLocal(img_suc,ImageUtil.modifyImagetogeter(b, d)); 
 WXControllerLog.INFO("合并图片,活动图片的地址tgImageUrl:"+tgImageUrl);
 WXControllerLog.INFO("合并图片,合并后的地址img_suc:"+img_suc);

合成后的图片如下:



3、推广图片和二维码合并生成一个图片工具类。


public class ImageUtil {
 /**
 * 把两张图片合并
 */
 private static Font font = new Font("宋体", Font.PLAIN, 12); // 添加字体的属性设置 
 private static Graphics2D g = null;
 private static int fontsize = 0;
 private static int x = 0;
 private static int y = 0;
 /** 
 * 导入本地图片到缓冲区 
 */
 public static BufferedImage loadImageLocal(String imgName) {
 try {
 return ImageIO.read(new File(imgName));
 } catch (IOException e) {
 System.out.println(e.getMessage());
 }
 return null;
 }
 /** 
 * 导入网络图片到缓冲区 
 */
 public static BufferedImage loadImageUrl(String imgName) {
 try {
 URL url = new URL(imgName);
 return ImageIO.read(url);
 } catch (IOException e) {
 System.out.println(e.getMessage());
 }
 return null;
 }
 /** 
 * 生成新图片到本地 
 */
 public static void writeImageLocal(String newImage, BufferedImage img) {
 if (newImage != null && img != null) {
 try {
 File outputfile = new File(newImage);
 ImageIO.write(img, "jpg", outputfile);
 } catch (IOException e) {
 System.out.println(e.getMessage());
 }
 }
 }
 /** 
 * 设定文字的字体等 
 */
 public static void setFont(String fontStyle, int fontSize) {
 fontsize = fontSize;
 font = new Font(fontStyle, Font.PLAIN, fontSize);
 }
 /** 
 * 修改图片,返回修改后的图片缓冲区(只输出一行文本) 
 */
 public static BufferedImage modifyImage(BufferedImage img, Object content, int x, int y) {
 try {
 int w = img.getWidth();
 int h = img.getHeight();
 g = img.createGraphics();
 g.setBackground(Color.WHITE);
 g.setColor(Color.orange);//设置字体颜色 
 if (font != null)
 g.setFont(font);
 // 验证输出位置的纵坐标和横坐标 
 if (x >= h || y >= w) {
 x = h - fontsize + 2;
 y = w;
 } else {
 x = x;
 y = y;
 }
 if (content != null) {
 g.drawString(content.toString(), x, y);
 }
 g.dispose();
 } catch (Exception e) {
 System.out.println(e.getMessage());
 }
 return img;
 }
 /** 
 * 修改图片,返回修改后的图片缓冲区(输出多个文本段) xory:true表示将内容在一行中输出;false表示将内容多行输出 
 */
 public BufferedImage modifyImage(BufferedImage img, Object[] contentArr, int x, int y,
 boolean xory) {
 try {
 int w = img.getWidth();
 int h = img.getHeight();
 g = img.createGraphics();
 g.setBackground(Color.WHITE);
 g.setColor(Color.RED);
 if (this.font != null)
 g.setFont(this.font);
 // 验证输出位置的纵坐标和横坐标 
 if (x >= h || y >= w) {
 this.x = h - this.fontsize + 2;
 this.y = w;
 } else {
 this.x = x;
 this.y = y;
 }
 if (contentArr != null) {
 int arrlen = contentArr.length;
 if (xory) {
 for (int i = 0; i < arrlen; i++) {
 g.drawString(contentArr[i].toString(), this.x, this.y);
 this.x += contentArr[i].toString().length() * this.fontsize / 2 + 5;// 重新计算文本输出位置 
 }
 } else {
 for (int i = 0; i < arrlen; i++) {
 g.drawString(contentArr[i].toString(), this.x, this.y);
 this.y += this.fontsize + 2;// 重新计算文本输出位置 
 }
 }
 }
 g.dispose();
 } catch (Exception e) {
 System.out.println(e.getMessage());
 }
 return img;
 }
 /** 
 * 修改图片,返回修改后的图片缓冲区(只输出一行文本) 
 */
 public BufferedImage modifyImageYe(BufferedImage img) {
 try {
 int w = img.getWidth();
 int h = img.getHeight();
 g = img.createGraphics();
 g.setBackground(Color.WHITE);
 g.setColor(Color.blue);//设置字体颜色 
 if (this.font != null)
 g.setFont(this.font);
 g.drawString("www.hi.baidu.com?xia_mingjian", w - 85, h - 5);
 g.dispose();
 } catch (Exception e) {
 System.out.println(e.getMessage());
 }
 return img;
 }
 public static BufferedImage modifyImagetogeter(BufferedImage b, BufferedImage d) {
 try {
 int w = 110;//b.getWidth();
 int h = 110;//b.getHeight();
 g = d.createGraphics();
 g.drawImage(b, 515, 830, w, h, null);
 g.dispose();
 } catch (Exception e) {
 System.out.println(e.getMessage());
 }
 return d;
 }
}

4,生成的图片上传微信服务器,获取media_id,数据库记录信息。

推送微信图片消息,需要获取到图片的media_id,所以需要把合成的图片上传微信获取media_id。并保存信息,再次推送图片消息的时候可以直接数据库查询。数据库记录唯一标识、medis_id、用户消息、公众号标识等


//获取token
 WxConfig wxConfig = wxConfigDao.queryByType("accessToken");
 String token =wxConfig.getExtends0();
 //图片上传至微信服务器
 String uploadMeida = WXUtil.Uploadmaterial( "image", img_suc, token);
 WXControllerLog.INFO("图片上传至微信服务器,微信返回信息:"+uploadMeida);
 JSONObject parseObject = JSONObject.parseObject(uploadMeida);
 try {
 WxAutoReply wr = wxAutoReplyDao.findAutoReplyByOpenid(keyWord, tencent,fromUserName);
 if(wr==null){//保存信息
 wr = new WxAutoReply();
 wr.setKeyword(keyWord);
 wr.setKeyValue(parseObject.getString("media_id"));
 wr.setTencent(tencent);
 wr.setMsgtype("image");
 wr.setExtends3(keyWord.split("\\+")[1]);
 wr.setExtends4(randomString);
 wr.setExtends6(fromUserName);
 wxAutoReplyDao.saveWxAutoReply(wr);
 }else{
 wr.setKeyword(keyWord);
 wr.setKeyValue(parseObject.getString("media_id"));
 wr.setTencent(tencent);
 wr.setMsgtype("image");
 wr.setExtends3(keyWord.split("\\+")[1]);
 wr.setExtends4(randomString);
 wr.setExtends6(fromUserName);
 wxAutoReplyDao.updateWxAutoReply(wr);
 }
 } catch (Exception e) {
 e.printStackTrace();
 }

5、根据media_id给用户退送推广图片。

调用微信接口推送消息


//推送信息
 String sendMsg ="{\"touser\":\""+fromUserName+"\",\"msgtype\": \"image\",\"image\":{\"media_id\":\""+parseObject.getString("media_id")+"\"}}";
 String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+token;
 String responseJson = HttpUtil.postXmlRequest(url, sendMsg,"utf-8");
 WXControllerLog.INFO("SendWxMessageThread发送微信消息,返回信息:"+responseJson);

6、最后的效果,如图:



7、用户出单以后,数据库记录埋码标识。

只要在用户出单的时候,记录唯一标识randomString就可以了


注:其实需要处理的问题还是挺多的,篇幅有限就只粘贴关键代码了,我觉得上面的工具栏贼好用就直接全部分享了。

微信开发中遇到的相关问题可以留言或者私信,有问必答。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表