工具类-21-企业微信自助QA机器人接入

文章目录

  • 1. 需求背景
  • 2. 相关文档整理
  • 3. 接入流程演示
    • 3.1 拉群,添加机器人
    • 3.2 给机器人取个名字
    • 3.3 点击配置说明
    • 3.4 配置 接收消息配置 信息
  • 4. 代码演示

1. 需求背景

在企业当中,经常会被多次问到相同的问题,而我们都有自己的其他需求需要做,于是为了提高办公效率,缩短沟通成本,我们可以在企业微信里面拉一个群,创建一个机器人,通过@企业机器人自助问答的方式获取想要的结果;

2. 相关文档整理

  • 添加机器人
    https://developer.work.weixin.qq.com/document/path/91770
  • 如何接受群里面艾特机器人后输入的聊天内容https://developer.work.weixin.qq.com/document/path/90930
  • 如何发送信息到群群
    https://developer.work.weixin.qq.com/document/path/91770
  • 加解密相关
    https://developer.work.weixin.qq.com/document/path/90968
    https://developer.work.weixin.qq.com/document/path/90468

获取用户相关(企微机器人只能通过聊天信息拿到userid,如果需要通过userid获取用户信息,需要申请企微应用然后调用企微通讯录相关api来获取,企微应用可以在流程中心进行企微应用申请,然后联系it的同学获取更多信息)

  • 申请企业微信应用以及应用的一些基本概念
    https://developer.work.weixin.qq.com/document/path/90665

  • 获取用户信息(注意因为保密原因企微通讯录拿不到用户email,你还需要和sso团队去拿到相关api来进行用户信息获取)
    https://developer.work.weixin.qq.com/document/path/90196

  • 提示

    • 接受企业微信的聊天内容需要一个公网可达的接收点
    • 加解密中有一个corpid参数是企业微信中识别企业的id,这个可以联系it的同学获取
    • 企业微信文档那是非常差,需要多阅读和动手尝试,有问题可以在企业微信的社区提交问题咨询
    • 多阅读企业微信的api文档

3. 接入流程演示

3.1 拉群,添加机器人

在这里插入图片描述

3.2 给机器人取个名字

在这里插入图片描述

3.3 点击配置说明

在这里插入图片描述

3.4 配置 接收消息配置 信息

在这里插入图片描述

  • 第一栏URL 必须是外网可以访问的,且该接口已经在线上可以直接访问,所以配置该信息之前,你的线上环境已经有相关接口了
  • 第二栏的 Token 随机生成即可,后续需要配置在代码里面
  • 第三栏 和 第二栏一样,随机生成即可,后续需要配置在代码里面

讲一下流程:如上图所示,当点击保存的时候,机器人后台会去check一下输入的url,这个url会承担两部分功能,第一个是check一下url是否可用,第二个是当有人在群里@机器人时,会回调该接口,因此下面会出现两个相同url接口但是不同的接收参数形式:

4. 代码演示

Controller层:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import static org.springframework.util.MimeTypeUtils.APPLICATION_XML_VALUE;@RestController
@RequestMapping(value = "/chat")
@Slf4j
public class ChatRobotController {@Autowiredprivate ChatRobotService chatRobotService;/*** 支持Http Get请求验证URL有效性** @param msgSignature 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体* @param timestamp    时间戳。与nonce结合使用,用于防止请求重放攻击* @param nonce        随机数。与timestamp结合使用,用于防止请求重放攻击* @param echoStr      加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、receiveid四个字段,其中msg即为消息内容明文* @return 回调服务需要作出正确的响应*/@GetMapping(value = "/notify")public String verifyUrl(@RequestParam("msg_signature") String msgSignature,@RequestParam("timestamp") Integer timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echoStr) {log.info("verifyUrl--> msgSignature:{},timestamp:{},nonce:{},echoStr:{}", msgSignature, timestamp, nonce, echoStr);return chatRobotService.verifyUrl(msgSignature, timestamp, nonce, echoStr);}/*** 支持Http Post请求接收业务数据* 当用户触发回调行为时,企业微信会发送回调消息到填写的URL,请求内容** @param msgSignature 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体* @param timestamp    时间戳。与nonce结合使用,用于防止请求重放攻击* @param nonce        随机数。与timestamp结合使用,用于防止请求重放攻击。* @param dto          报文数据*/@PostMapping(value = "/notify", consumes = APPLICATION_XML_VALUE)public void callBack(@RequestParam("msg_signature") String msgSignature,@RequestParam("timestamp") Integer timestamp,@RequestParam("nonce") String nonce,@RequestBody ChatRobotCallBackDTO dto) {log.info("callBack-->msgSignature:{},timestamp:{},nonce:{},dto:{}", msgSignature, timestamp, nonce, JsonUtil.toJSON(dto));chatRobotService.callBack(msgSignature, timestamp, nonce, dto);}
}

Service层:

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static cn.hutool.core.util.XmlUtil.readXML;/*** 企微机器人** @author wql* @date 2023/4/23 10:19*/
@Service
@Slf4j
public class ChatRobotServiceImpl implements ChatRobotService {private final static String TOKEN = "iLOUyBRIN6CWScZtPqHF15";@Overridepublic String verifyUrl(String msgSignature, Integer timestamp, String nonce, String echoStr) {String signature = getSignature(TOKEN, String.valueOf(timestamp), nonce, echoStr);if (!Objects.equals(signature, msgSignature)) {throw new VerifyException(VerifyException.GET_SIGNATURE_ERROR);}return getVerifyUrlContent(getOriginByte(echoStr));}/*** <xml>*     <From>*         <UserId>*             <![CDATA[wo-ApVEAAANKrX-iuxI9zy34Enju8YeQ]]>*         </UserId>*     </From>*     <WebhookUrl>*         <![CDATA[https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6132293c-dfcd-4b0e-a79c-b0009362e51e]]>*     </WebhookUrl>*     <ChatId>*         <![CDATA[wr-ApVEAAAXa_Vwj9LjYp18C1jB_SPbA]]>*     </ChatId>*     <MsgId>*         <![CDATA[CIGABBDxnJOiBhjl1aHskICAAyCQAQ==]]>*     </MsgId>*     <ChatType>*         <![CDATA[group]]>*     </ChatType>*     <MsgType>*         <![CDATA[text]]>*     </MsgType>*     <Text>*         <Content>*             <![CDATA[@TestRobot 1234567]]>*         </Content>*     </Text>* </xml>*/@Overridepublic void callBack(String msgSignature, Integer timestamp, String nonce, ChatRobotCallBackDTO dto) {//content 报文如上所示String content = this.verifyUrl(msgSignature, timestamp, nonce, dto.getEncrypt());if (StrUtil.isNotBlank(content)) {String text = readXML(content).getElementsByTagName("Text").item(0).getFirstChild().getTextContent();if (StrUtil.isNotBlank(text)) {List<String> textItems = Arrays.asList(text.split(" "));if (CollUtil.isNotEmpty(textItems)) {//0 是机器人, 1是输入的文本String queryText = textItems.get(1);//TODO 查询数据源并回答List<String> resultList = Lists.newArrayList();resultList.add("我叫李四");resultList.add("[这是一个链接](http://work.weixin.qq.com/api/doc)");WeChatBotUtils.sendMarkDown(queryText, resultList);}}}}public static void main(String[] args) {List<String> resultList = Lists.newArrayList();resultList.add("我叫李四");resultList.add("[这是一个链接](http://work.weixin.qq.com/api/doc)");WeChatBotUtils.sendMarkDown("我是张三", resultList);}
}

工具类:

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.TimeUnit;@Slf4j
public class WeChatBotUtils {private final static String ROBOT_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6132293c-dfcd-4b0e" +"-a79c-b00e";/*** 发送文字消息* <p>* {* "msgtype": "text",* "text": {* "content": "广州今日天气:29度,大部分多云,降雨概率:60%",* "mentioned_list":["wangng","@all"],* "mentioned_mobile_list":["13800001111","@all"]* }* }** @param msg 需要发送的消息*/public static String sendTextMsg(String msg) {JSONObject text = new JSONObject();text.put("content", msg);
//        ArrayList<String> users = Lists.newArrayList();
//        users.add("@all");
//        text.put("mentioned_list", users);JSONObject reqBody = new JSONObject();//本内容,最长不超过2048个字节,必须是utf8编码reqBody.put("text", text);//消息类型,此时固定为textreqBody.put("msgtype", "text");reqBody.put("safe", 0);return callWeChatBot(reqBody.toString());}/*** 发送图片消息,需要对图片进行base64编码并计算图片的md5值** @param path 需要发送的图片路径*/public static String sendImgMsg(String path) throws Exception {String base64 = "";String md5 = "";// 获取Base64编码try {FileInputStream inputStream = new FileInputStream(path);byte[] bs = new byte[inputStream.available()];inputStream.read(bs);base64 = Base64.getEncoder().encodeToString(bs);} catch (IOException e) {e.printStackTrace();}// 获取md5值try {FileInputStream inputStream = new FileInputStream(path);byte[] buf = new byte[inputStream.available()];inputStream.read(buf);md5 = DigestUtils.md5Hex(buf);} catch (IOException e) {e.printStackTrace();}JSONObject image = new JSONObject();image.put("base64", base64);image.put("md5", md5);JSONObject reqBody = new JSONObject();reqBody.put("msgtype", "image");reqBody.put("image", image);reqBody.put("safe", 0);return callWeChatBot(reqBody.toString());}/*** 发送MarKDown消息** @param question 需要发送的消息*/public static String sendMarkDown(String question, List<String> resultList) {JSONObject markdown = new JSONObject();String res = "您输入的关键字:\t<font color =\"warning\">" + question + "</font>\t机器人从文档库匹配出如下文档";if (CollUtil.isEmpty(resultList)) {res = res + "\n\t无法通过输入的关键词匹配到相关文档";}else {for (int i = 1; i <= resultList.size(); i++) {res = res + "\n\t" + i + ".\t" + resultList.get(i - 1);}}markdown.put("content", res);JSONObject reqBody = new JSONObject();reqBody.put("msgtype", "markdown");reqBody.put("markdown", markdown);reqBody.put("safe", 0);return callWeChatBot(reqBody.toString());}/*** 调用群机器人*/public static String callWeChatBot(String reqBody) {try {// 构造RequestBody对象,用来携带要提交的数据;需要指定MediaType,用于描述请求/响应 body 的内容类型MediaType contentType = MediaType.parse("application/json; charset=utf-8");RequestBody body = RequestBody.create(contentType, reqBody);// 调用群机器人String respMsg = okHttp(body, ROBOT_URL);if (StrUtil.isNotBlank(respMsg)) {int errCode = (Integer) getParamsFromJson(respMsg, "errcode");if (errCode == 0) {log.info("向群发送消息成功!");} else {log.info("向群发送消息失败!,错误信息为:{}", JSON.toJSONString(respMsg));}}return respMsg;} catch (Exception e) {log.error("群机器人推送消息失败:{}", ErrorUtil.getErrorStackTraceMsg(e));}return null;}private static Object getParamsFromJson(String json, String sourceKeyName) {Object object = null;try {object = JsonPath.read(json, "$." + sourceKeyName);} catch (PathNotFoundException e) {log.error(ErrorUtil.getErrorStackTraceMsg(e));}return object;}public static String okHttp(RequestBody body, String url) throws Exception {// 构造和配置OkHttpClientOkHttpClient client;client = new OkHttpClient.Builder()// 设置连接超时时间.connectTimeout(10, TimeUnit.SECONDS)// 设置读取超时时间.readTimeout(20, TimeUnit.SECONDS).build();// 构造Request对象Request request = new Request.Builder().url(url).post(body)// 响应消息不缓存.addHeader("cache-control", "no-cache").build();// 构建Call对象,通过Call对象的execute()方法提交异步请求Response response = null;try {response = client.newCall(request).execute();} catch (IOException e) {e.printStackTrace();}// 请求结果处理assert response != null;assert response.body() != null;byte[] datas = response.body().bytes();return new String(datas);}}

ChatRobotUtil


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;/*** 签名工具类** @author wql* @date 2023/4/23 10:30*/
@Slf4j
public class ChatRobotUtil {private final static Charset CHARSET = StandardCharsets.UTF_8;/*** 用SHA1算法生成安全签名** @param token     票据* @param timestamp 时间戳* @param nonce     随机字符串* @param encrypt   密文* @return 安全签名*/public static String getSignature(String token, String timestamp, String nonce, String encrypt) {try {String[] array = new String[]{token, timestamp, nonce, encrypt};StringBuilder sb = new StringBuilder();// 字符串排序Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();// SHA1签名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuilder hexstr = new StringBuilder();String shaHex = "";for (byte b : digest) {shaHex = Integer.toHexString(b & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {log.error(ErrorUtil.getErrorStackTraceMsg(e));throw new VerifyException(VerifyException.GET_SIGNATURE_ERROR);}}public static byte[] getOriginByte(String echoStr) {String encodingAesKey = ApolloUtil.getKey("robot.encodingAesKey", "4pGyYEdb0qfvLN1yyNhNvLzDcLaSLMlBCGo9Q5ixW8w");byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");byte[] original;try {// 设置解密模式为AES的CBC模式Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);// 使用BASE64对密文进行解码byte[] encrypted = Base64.decodeBase64(echoStr);// 解密original = cipher.doFinal(encrypted);} catch (Exception e) {log.error(ErrorUtil.getErrorStackTraceMsg(e));throw new VerifyException(VerifyException.PARSER_ECHO_STR_ERROR);}return original;}public static String getVerifyUrlContent(byte[] original) {String xmlContent;try {byte[] bytes = decode(original);byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength = recoverNetworkBytesOrder(networkOrder);xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);} catch (Exception e) {log.error(ErrorUtil.getErrorStackTraceMsg(e));throw new VerifyException(VerifyException.XML_CONTENT_ERROR);}return xmlContent;}private static int recoverNetworkBytesOrder(byte[] orderBytes) {int sourceNumber = 0;for (int i = 0; i < 4; i++) {sourceNumber <<= 8;sourceNumber |= orderBytes[i] & 0xff;}return sourceNumber;}private static byte[] decode(byte[] decrypted) {int pad = (int) decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}
}

errorUtil:

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;public class ErrorUtil {public ErrorUtil() {}public static String getErrorStackTraceMsg(Throwable error) {ByteArrayOutputStream baos = new ByteArrayOutputStream();error.printStackTrace(new PrintStream(baos));return baos.toString();}
}

DTO:

import lombok.Data;import java.io.Serializable;import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;/*** 当用户触发回调行为时,企业微信会发送回调消息到填写的URL,请求内容** @author wangqinglong01*/
@Data
public class ChatRobotCallBackDTO implements Serializable {private static final long serialVersionUID = -2513785018658444032L;/*** 企业微信的CorpID,当为第三方应用回调事件时,CorpID的内容为suiteid*/@JacksonXmlProperty(localName = "ToUserName")private String toUserName;/*** 接收的应用id,可在应用的设置页面获取。仅应用相关的回调会带该字段。*/@JacksonXmlProperty(localName = "AgentID")private String agentId;/*** 消息结构体加密后的字符串*/@JacksonXmlProperty(localName = "Encrypt")private String encrypt;}

需要引入的依赖

  compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/45056.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信中如何接入机器人才比较正常

大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。 前言 为什么会有这个话题?大家都知道最近有个AI机器人很火,那就是AI机器人,关于它的介绍,大家可以自行百度去,我这边就不多介绍了。 好多人嫌网页版玩的不过瘾,就把这个机器人接入到了QQ上,接入到了钉钉上,TG …

微信接入智能机器人回复消息

微信接入智能机器人回复消息 1 寻找智能机器人API 此处我使用的是极速数据 七款不错的聊天机器人API推荐 ①登录注册之后&#xff0c;直接搜索机器人 ②申请数据 ③在个人中心&#xff0c;找到自己的appKey ④编写代码&#xff0c;根据接口地址发起对应的HTTP请求即可 …

目标检测算法——YOLOv5/YOLOv7改进之结合无参注意力SimAM(涨点神器)

&#x1f496;&#x1f496;>>>加勒比海带&#xff0c;QQ2479200884<<<&#x1f496;&#x1f496; &#x1f340;&#x1f340;>>>【YOLO魔法搭配&论文投稿咨询】<<<&#x1f340; ✨✨>>>学习交流 | 温澜潮生 | 合作共赢…

chatgpt赋能Python-python_miio

Python-miio&#xff1a;探索小米智能设备的开发 Python-miio是一个开源的Python库&#xff0c;用于控制小米智能设备。小米智能设备包括智能扫地机器人、智能插座、智能空气净化器和智能灯泡等。Python-miio库支持局域网和互联网控制&#xff0c;是一个非常实用的工具&#x…

Python外星人入侵游戏——添加飞船和外星人图片

Python外星人入侵游戏是自己在《Python编程从入门到实践》在本书里学到的。本篇主要介绍该游戏中所需要的两个图片。分别为飞船和外星人图片。 1、首先去到 http://www.ituring.com.cn/book/1861 网站&#xff0c;就会看到有关Python编程从入门到实践这本书。 2、在右边有个随…

Stable Diffusion模型测试

文章目录 前言一、dqnapi 是什么&#xff1f;二、使用步骤1.本地2.在线测试 总结 前言 AI图像生成异常火爆&#xff0c;听说鹅厂都开始用AI图像生成做前期设定了&#xff0c;小厂更是直接用AI替代了原画师的岗位。这一张张丰富细腻、风格各异、以假乱真的AI生成图像&#xff0…

chatgpt赋能python:Python用于电力行业的应用与未来趋势

Python用于电力行业的应用与未来趋势 Python作为一种易学易用的编程语言&#xff0c;在不同行业中有着广泛的应用。本文将介绍Python在电力行业中的应用&#xff0c;重点讨论其在用电量分析和预测方面的优势&#xff0c;并探讨未来Python在电力行业中的应用趋势。 Python在用…

ChatGPT在能源行业的预测场景:智能能源管理和异常检测的未来趋势

第一章&#xff1a;引言 能源是现代社会发展的关键驱动力之一&#xff0c;然而&#xff0c;传统的能源管理方法存在许多挑战&#xff0c;如能源浪费、供需不平衡以及能源异常等。为了应对这些挑战&#xff0c;智能能源管理系统逐渐崭露头角。在本文中&#xff0c;我们将探讨Ch…

如何申请成为openai chatgpt的alpha内测人员

今天登录账号时意外发现侧边栏有个take survey链接写着wed love to hear from you. shape the future of chatgpt. 点进去首页显示这个 ChatGPT User Survey 感谢您参与我们关于ChatGPT的用户研究调查&#xff01;我们感谢您的时间和反馈&#xff0c;这将帮助我们提高产品质量…

达摩院开源人脸检测榜首模型MogFace

01 开源 论文链接 https://openaccess.thecvf.com/content/CVPR2022/papers/Liu_MogFace_Towards_a_Deeper_Appreciation_on_Face_Detection_CVPR_2022_paper.pdf 模型&代码 https://modelscope.cn/models/damo/cv_resnet101_face-detection_cvpr22papermogface/summary 简…

聊聊并发——生产者消费者模式

(转自&#xff1a;http://www.infoq.com/cn/articles/producers-and-consumers-mode?utm_sourceinfoq&utm_campaignuser_page&utm_mediumlink) 在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的…

阿里内贸团队敏捷实践(二)自组织管理

实现团队的自组织管理&#xff0c;非常有助于团队形成合力&#xff0c;极大地提升团队整体的工作效率。本文结合原阿里ITU内贸团队的敏捷实践经历&#xff0c;阐释了何为自组织管理、为什么进行自组织管理、如何进行自组织管理等内容&#xff0c;同时给出了团队实施自组织管理的…

阿里内贸团队敏捷实践(三)结对编程

本文主要从提升项目质量、促进知识传递及减少项目风险等角度出发&#xff0c;讲述作者所在团队在结对编程实践中的一些经历&#xff0c;以及如何避免或减少其所带来的负面影响。 你了解结对编程吗&#xff1f;你尝试过结对编程实践吗&#xff1f;也许你还未曾尝试甚至还不曾了解…

聊聊生产者消费者模式

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使用生产者和消费者模式 在线程世界里&#xff0c;生产者就是生产数据的线程&#xff0c;消费者就是消费数据的线程。在…

聊聊并发 生产者消费者模式

http://ifeve.com/producers-and-consumers-mode/ 本文首发于InfoQ 作者&#xff1a;方腾飞 校对&#xff1a;张龙 在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使…

阿里内贸团队敏捷实践-敏捷回顾

回顾review是敏捷开发中的一个必不可少的实践也是把整个敏捷开发过程连接成一个闭环的关键节点本文将阐述我们是如何做敏捷回顾的。 敏捷回顾最高指导原则 无论我们发现了什么考虑到当时的已知情况、个人的技术水平和能力、可用的资源以及手上的状况我们理解并坚信每个人对自己…

java 生产者消费者模式_聊聊并发(十)生产者消费者模式

本文首发于InfoQ 作者&#xff1a;方腾飞 校对&#xff1a;张龙 在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使用生产者和消费者模式 在线程世界里&#xff0c;生产…

基于Trtc的内贸站视频聊天服务【二】

基于Trtc的内贸站视频聊天服务【二】 上一节课和大家聊了一下web端视频聊天的技术演变和发展&#xff0c;需要满足web端视频聊天的基本条件。以及介绍了一下腾讯云提供的Trtc服务&#xff0c;大概说了下腾讯云的sdk。本节课就以实际开发内贸站视频聊天的项目&#xff08;Swan&…

谷歌外贸sem与百度内贸sem的不同

1&#xff0c;国内的话&#xff0c;不用在乎是否使用在家用&#xff0c;起订量问题一般不用特别注意&#xff0c;如果家用零售的话&#xff0c;大家会很自觉地想到淘宝&#xff0c;拼多多。但是进出口的话&#xff0c;必须是商用&#xff0c;批发&#xff0c;大批量货物类型&am…

外贸软件进出口内贸综合型管理解决方案

外贸公司综合型业务模式&#xff0c;指的是公司涉及自营、代理进出口业务、内贸业务、转口业务等等多业务模式&#xff0c;涉及的产品种类多&#xff0c;像这样的综合型外贸公司就需要通过信息化管理实现业财一体化&#xff0c;完善资金流向&#xff0c;简化工作流程&#xff0…