From 5ff781e1336374505403df1285f82b0d600b2ffb Mon Sep 17 00:00:00 2001
From: xiang2lin <251481237@qq.com>
Date: Tue, 24 Sep 2024 08:42:44 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
fw-weixin/pom.xml | 54 ++++
.../src/main/java/com/hzya/frame/Main.java | 5 +
.../frame/wecom/service/IWeComService.java | 54 ++++
.../wecom/service/impl/WeComServiceImpl.java | 243 ++++++++++++++++++
.../frame/wecom/util/WeComAccessToken.java | 67 +++++
.../target/classes/com/hzya/frame/Main.class | Bin 0 -> 576 bytes
.../classes/com/hzya/frame/wecom/Test.class | Bin 0 -> 279 bytes
.../frame/wecom/service/IWeComService.class | Bin 0 -> 428 bytes
.../wecom/service/impl/WeComServiceImpl.class | Bin 0 -> 5880 bytes
.../frame/wecom/util/WeComAccessToken.class | Bin 0 -> 2807 bytes
10 files changed, 423 insertions(+)
create mode 100644 fw-weixin/pom.xml
create mode 100644 fw-weixin/src/main/java/com/hzya/frame/Main.java
create mode 100644 fw-weixin/src/main/java/com/hzya/frame/wecom/service/IWeComService.java
create mode 100644 fw-weixin/src/main/java/com/hzya/frame/wecom/service/impl/WeComServiceImpl.java
create mode 100644 fw-weixin/src/main/java/com/hzya/frame/wecom/util/WeComAccessToken.java
create mode 100644 fw-weixin/target/classes/com/hzya/frame/Main.class
create mode 100644 fw-weixin/target/classes/com/hzya/frame/wecom/Test.class
create mode 100644 fw-weixin/target/classes/com/hzya/frame/wecom/service/IWeComService.class
create mode 100644 fw-weixin/target/classes/com/hzya/frame/wecom/service/impl/WeComServiceImpl.class
create mode 100644 fw-weixin/target/classes/com/hzya/frame/wecom/util/WeComAccessToken.class
diff --git a/fw-weixin/pom.xml b/fw-weixin/pom.xml
new file mode 100644
index 00000000..5a3c515b
--- /dev/null
+++ b/fw-weixin/pom.xml
@@ -0,0 +1,54 @@
+
+
+
+ com.hzya.frame
+ kangarooDataCenterV3
+ ${revision}
+
+
+ 4.0.0
+ fw-weixin
+ jar
+ ${revision}
+
+
+
+ com.hzya.frame
+ base-service
+ ${revision}
+
+
+ mysql
+ mysql-connector-java
+ ${mysql-connector-java}
+
+
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ none
+ execute
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw-weixin/src/main/java/com/hzya/frame/Main.java b/fw-weixin/src/main/java/com/hzya/frame/Main.java
new file mode 100644
index 00000000..1817eb3e
--- /dev/null
+++ b/fw-weixin/src/main/java/com/hzya/frame/Main.java
@@ -0,0 +1,5 @@
+package com.hzya.frame;public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+}
\ No newline at end of file
diff --git a/fw-weixin/src/main/java/com/hzya/frame/wecom/service/IWeComService.java b/fw-weixin/src/main/java/com/hzya/frame/wecom/service/IWeComService.java
new file mode 100644
index 00000000..aec0dd6d
--- /dev/null
+++ b/fw-weixin/src/main/java/com/hzya/frame/wecom/service/IWeComService.java
@@ -0,0 +1,54 @@
+package com.hzya.frame.wecom.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.hzya.frame.sysnew.application.entity.SysExtensionApiEntity;
+import com.hzya.frame.web.entity.JsonResultEntity;
+
+/**
+ * @Description 企业微信service
+ * @Author xiangerlin
+ * @Date 2024/9/23 14:23
+ **/
+public interface IWeComService {
+ /**
+ * 获取accessToken
+ * 该方法会缓存accessToken
+ * https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
+ * @param jsonObject
+ * @return
+ */
+ JSONObject accessToken(JSONObject jsonObject);
+
+ /**
+ * 发送消息
+ * https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
+ * @param jsonObject
+ * @return
+ */
+ JSONObject messageSend(JSONObject jsonObject);
+
+ /**
+ * 根据授权码获取用户信息
+ * 单点登录的时候用
+ * https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+ * @param jsonObject
+ * @return
+ */
+ JSONObject getUserInfoByAuthCode(JSONObject jsonObject);
+
+ /**
+ * 根据userid读取成员信息
+ * https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID
+ * @param jsonObject
+ * @return
+ */
+ JSONObject getUserInfoByUserId(JSONObject jsonObject);
+
+ /**
+ * 根据手机号获取userid
+ * https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN
+ * @param jsonObject
+ * @return
+ */
+ JSONObject getUserIdByMobile(JSONObject jsonObject);
+}
diff --git a/fw-weixin/src/main/java/com/hzya/frame/wecom/service/impl/WeComServiceImpl.java b/fw-weixin/src/main/java/com/hzya/frame/wecom/service/impl/WeComServiceImpl.java
new file mode 100644
index 00000000..f8b36791
--- /dev/null
+++ b/fw-weixin/src/main/java/com/hzya/frame/wecom/service/impl/WeComServiceImpl.java
@@ -0,0 +1,243 @@
+package com.hzya.frame.wecom.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+import com.hzya.frame.sysnew.application.entity.SysExtensionApiEntity;
+import com.hzya.frame.web.entity.BaseResult;
+import com.hzya.frame.web.entity.JsonResultEntity;
+import com.hzya.frame.wecom.service.IWeComService;
+import com.hzya.frame.wecom.util.WeComAccessToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description 企业微信service
+ * @Author xiangerlin
+ * @Date 2024/9/23 14:24
+ **/
+@Service(value = "weComServiceImpl")
+public class WeComServiceImpl implements IWeComService {
+ static Logger logger = LoggerFactory.getLogger(WeComServiceImpl.class);
+ /**
+ * 获取accessToken
+ * https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
+ * @param json
+ * @return
+ */
+ @Override
+ public JSONObject accessToken(JSONObject json) {
+ JSONObject jsonObject = json.getJSONObject("jsonStr");
+ if (null == jsonObject){
+ return this.error("参数不能为空");
+ }
+ String corpid = jsonObject.getString("corpid");
+ String corpsecret = jsonObject.getString("corpsecret");
+ if (StrUtil.isEmpty(corpid)) {
+ return this.error("corpid不能为空");
+ }
+ if (StrUtil.isEmpty(corpsecret)) {
+ return this.error("corpsecret不能为空");
+ }
+ try {
+ String accessToken = WeComAccessToken.getAccessToken(corpid, corpsecret);
+ return this.ok(accessToken);
+ }catch(Exception e){
+ logger.error("获取accessToken出错",e);
+ }
+ return this.error("系统异常");
+ }
+
+ /**
+ * 发送消息
+ * https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
+ * @param json
+ * @return
+ */
+ @Override
+ public JSONObject messageSend(JSONObject json) {
+ JSONObject jsonObject = json.getJSONObject("jsonStr");
+ if (null == jsonObject){
+ return this.error("参数不能为空");
+ }
+ String agentid = jsonObject.getString("agentid");
+ String text = jsonObject.getString("text");
+ String access_token = jsonObject.getString("access_token");
+ if (StrUtil.isEmpty(agentid)){
+ return this.error("agentid不能为空");
+ }
+ if (StrUtil.isEmpty(text)){
+ return this.error("消息内容不能为空");
+ }
+ if (StrUtil.isEmpty(access_token)){
+ return this.error("access_token不能为空");
+ }
+ String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token="+access_token;
+ jsonObject.remove("access_token");
+ String param = jsonObject.toString();
+ String res = HttpRequest.post(url).body(param).timeout(30000).execute().body();
+ if (StrUtil.isNotEmpty(res)){
+ JSONObject msgResponse = JSONObject.parseObject(res);
+ String errcode = msgResponse.getString("errcode");
+ String errmsg = msgResponse.getString("errmsg");
+ if ("0".equals(errcode)){
+ return ok();
+ }else {
+ return error(errmsg);
+ }
+ }
+ return this.error("操作失败");
+ }
+
+ /**
+ * 根据授权码获取用户信息
+ * 单点登录的时候用
+ * https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+ *
+ * @param json
+ * @return
+ */
+ @Override
+ public JSONObject getUserInfoByAuthCode(JSONObject json) {
+ JSONObject jsonObject = json.getJSONObject("jsonStr");
+ if (null == jsonObject){
+ return this.error("参数不能为空");
+ }
+ String access_token = jsonObject.getString("access_token");
+ String code = jsonObject.getString("code");
+ if (StrUtil.isEmpty(access_token)){
+ return error("access_token不能为空");
+ }
+ if (StrUtil.isEmpty(code)){
+ return error("code不能为空");
+ }
+ String url = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token="+access_token+"&code="+code;
+ String res = HttpRequest.get(url).timeout(30000).execute().body();
+ if (StrUtil.isNotEmpty(res)){
+ JSONObject msgResponse = JSONObject.parseObject(res);
+ String errcode = msgResponse.getString("errcode");
+ String errmsg = msgResponse.getString("errmsg");
+ String userid = msgResponse.getString("userid");
+ if ("0".equals(errcode)){
+ return ok(userid);
+ }else {
+ return error(errmsg);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 根据userid读取成员信息
+ * https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID
+ *
+ * @param json
+ * @return
+ */
+ @Override
+ public JSONObject getUserInfoByUserId(JSONObject json) {
+ JSONObject jsonObject = json.getJSONObject("jsonStr");
+ if (null == jsonObject){
+ return this.error("参数不能为空");
+ }
+ String access_token = jsonObject.getString("access_token");
+ String userid = jsonObject.getString("userid");
+ if (StrUtil.isEmpty(access_token)){
+ return error("access_token不能为空");
+ }
+ if (StrUtil.isEmpty(userid)){
+ return error("userid不能为空");
+ }
+ String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token="+access_token+"&userid="+userid;
+ String res = HttpRequest.get(url).timeout(30000).execute().body();
+ if (StrUtil.isNotEmpty(res)){
+ JSONObject msgResponse = JSONObject.parseObject(res);
+ String errcode = msgResponse.getString("errcode");
+ String errmsg = msgResponse.getString("errmsg");
+ if ("0".equals(errcode)){
+ return ok(res);
+ }else {
+ return error(errmsg);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 根据手机号获取userid
+ * https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN
+ *
+ * @param json
+ * @return
+ */
+ @Override
+ public JSONObject getUserIdByMobile(JSONObject json) {
+ JSONObject jsonObject = json.getJSONObject("jsonStr");
+ if (null == jsonObject){
+ return this.error("参数不能为空");
+ }
+ String access_token = jsonObject.getString("access_token");
+ String mobile = jsonObject.getString("mobile");
+ if (StrUtil.isEmpty(access_token)){
+ return error("access_token不能为空");
+ }
+ if (StrUtil.isEmpty(mobile)){
+ return error("mobile不能为空");
+ }
+ String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token="+access_token;
+ jsonObject.remove("access_token");
+ String param = jsonObject.toString();
+ String res = HttpRequest.post(url).body(param).timeout(30000).execute().body();
+ if (StrUtil.isNotEmpty(res)){
+ JSONObject msgResponse = JSONObject.parseObject(res);
+ String errcode = msgResponse.getString("errcode");
+ String errmsg = msgResponse.getString("errmsg");
+ String userid = msgResponse.getString("userid");
+ if ("0".equals(errcode)){
+ return ok(userid);
+ }else {
+ return error(errmsg);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 成功
+ * @return
+ */
+ private static JSONObject ok(){
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("code","200");
+ jsonObject.put("msg","成功");
+ jsonObject.put("data","");
+ return jsonObject;
+ }
+
+ /**
+ * 成功
+ * @param data 返回数据
+ * @return
+ */
+ private static JSONObject ok(String data){
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("code","200");
+ jsonObject.put("msg","成功");
+ jsonObject.put("data",data);
+ return jsonObject;
+ }
+
+ /**
+ * 失败
+ * @param msg 失败原因
+ * @return
+ */
+ private static JSONObject error(String msg){
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("code","500");
+ jsonObject.put("msg",msg);
+ jsonObject.put("data","");
+ return jsonObject;
+ }
+}
diff --git a/fw-weixin/src/main/java/com/hzya/frame/wecom/util/WeComAccessToken.java b/fw-weixin/src/main/java/com/hzya/frame/wecom/util/WeComAccessToken.java
new file mode 100644
index 00000000..78b6dc19
--- /dev/null
+++ b/fw-weixin/src/main/java/com/hzya/frame/wecom/util/WeComAccessToken.java
@@ -0,0 +1,67 @@
+package com.hzya.frame.wecom.util;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.time.Instant;
+
+/**
+ * @Description 获取企业微信accesToken
+ * @Author xiangerlin
+ * @Date 2024/9/23 14:08
+ **/
+public class WeComAccessToken {
+
+ static Logger logger = LoggerFactory.getLogger(WeComAccessToken.class);
+ //token
+ private static String accessToken;
+ //过期时间
+ private static Instant expireTime;
+ private static final Long CACHE_EXPIRY_TIME = 7000L; // 缓存有效时间(秒)
+
+
+ /**
+ * 获取accessToken
+ *
+ * @param corpid 企业ID
+ * @param corpsecret 应用的凭证密钥
+ * @return
+ */
+ public static String getAccessToken(String corpid,String corpsecret) {
+ //判断是否过期 如果没过期直接返回
+ if (null != accessToken && expireTime != null && Instant.now().isBefore(expireTime)) {
+ return accessToken;
+ }
+ //获取新的accessToken
+ accessToken = fetchNewAccessToken(corpid,corpsecret);
+ //过期时间设置成当前事件+7000s,预留200s的时间
+ expireTime = Instant.now().plusSeconds(CACHE_EXPIRY_TIME);
+ return accessToken;
+ }
+
+ /**
+ * 获取信的token
+ * @param corpid
+ * @param corpsecret
+ * @return
+ */
+ private static String fetchNewAccessToken(String corpid, String corpsecret) {
+ String url = " https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpid+"&corpsecret="+corpsecret;
+ String response = HttpRequest.get(url).timeout(30000).execute().body();
+ if (StrUtil.isNotEmpty(response)){
+ JSONObject json = JSONObject.parseObject(response);
+ String accessToken = json.getString("access_token");
+ return accessToken;
+ }
+ return null;
+ }
+
+ public static void main(String[] args) {
+ for (int i=0; i<2; i++){
+ String accessToken1 = WeComAccessToken.getAccessToken("wwb46c3f5e6ffe3e2b", "oON2ELxNVyl7wc37LeA9bNOsv_jyuFXdrvD9e0yogbQ");
+ System.out.println(accessToken1);
+ }
+ }
+}
diff --git a/fw-weixin/target/classes/com/hzya/frame/Main.class b/fw-weixin/target/classes/com/hzya/frame/Main.class
new file mode 100644
index 0000000000000000000000000000000000000000..fcf604dc32d90998f8f726d809a0b84c22d4e9c1
GIT binary patch
literal 576
zcmZuuyH3ME5S+`)u`whB!ut`R2^6?Nq5#n#A&T&jg3?f3k|Q}}`;g;+;IE(niGmN{
zqYz_*6p&)cyWZKonH}Hf*ZT*6J#0BBplV|d^F=H;Sj3WrWeY0;g+m?c_(&jI-);%y
zj!jnylo~oz=fj|_qAS_FT9bh410
zi0mf3E-Vx+th!jkx`2DCeBaa_OyqYrEYw_VV3R6icBIN^v5WSt>csz`=Hnn%0soB|
zvQhnUp^bM*3o(r-8PMiT%JCl-uQYZI^#6}s@#4CLBG|xmw
Ij2X;+1H{>R5C8xG
literal 0
HcmV?d00001
diff --git a/fw-weixin/target/classes/com/hzya/frame/wecom/Test.class b/fw-weixin/target/classes/com/hzya/frame/wecom/Test.class
new file mode 100644
index 0000000000000000000000000000000000000000..32e87025ee472c7091e1e997a4904190ce3d2942
GIT binary patch
literal 279
zcmZ{ey>7xl5QJxqe}XZ=qC=_ZAVsc87nBBx%7Oxm{(Ky8$Ul-XLFBDcMWWyV@=#Is
z5N&p)(axvc(f<5it^i&UhiIW2;4wgt;7?3xns-8LG+q(hX|>jbUSdi;KNgv;Q<>#V
z`bni^zLK@E{tn${XAXp?L{){@eV(P*)Uwdxq%BDGp?M>O%j#GwJu^0@ZQ;vae#i(e
zJi_4LQ$lR_L@vv%SY&&x8Vup^^R|ea16X}_;^qPG`5W}bz(K(3GX@^Gf#^1SXd`4t
J=&(A7{s5q@I*0%O
literal 0
HcmV?d00001
diff --git a/fw-weixin/target/classes/com/hzya/frame/wecom/service/IWeComService.class b/fw-weixin/target/classes/com/hzya/frame/wecom/service/IWeComService.class
new file mode 100644
index 0000000000000000000000000000000000000000..4914de8e1c430e2b1ec33525303f9954a4e994d7
GIT binary patch
literal 428
zcmaiwy-veG5QOJK5`)7(;vIm3Yv`y*BBUTfkPMNz
zGt&0@{9lK_;goD*qDNJ!f-F_FfbVkF5nNhurHUz_G7>nA$v)RqP0G0$Ha82Kj7&rV
zCmVd_VE554jfTzCglW!BKQN0*)9p$ePhMzbih!zRs_8OQOC@mO&Pv`JzRB$*-^c+j
zbso7f!RxyjnX59E>y7&Y0v&!F(CZ3pq074Glt3R~e|
literal 0
HcmV?d00001
diff --git a/fw-weixin/target/classes/com/hzya/frame/wecom/service/impl/WeComServiceImpl.class b/fw-weixin/target/classes/com/hzya/frame/wecom/service/impl/WeComServiceImpl.class
new file mode 100644
index 0000000000000000000000000000000000000000..cab0982e9d8e6924e4e2052c54105b875784532b
GIT binary patch
literal 5880
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