From 9c1c18b71d9a40ca76c12efa8b8e96580936500e Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 11 Nov 2024 00:02:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=9F=BA=E6=9C=AC=E4=BA=AC?= =?UTF-8?q?=E7=B2=89=E5=8A=9F=E8=83=BD=E6=8E=A8=E9=80=81=EF=BC=8C=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E7=BB=9F=E8=AE=A1=EF=BC=8C=E8=AE=A2=E5=8D=95=E6=8B=89?= =?UTF-8?q?=E5=8F=96=E7=9A=84=E7=A8=B3=E5=AE=9A=E7=89=88=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dataSources.xml | 17 +- .idea/jpa.xml | 9 + .idea/misc.xml | 5 +- .idea/sqldialects.xml | 1 - .idea/uiDesigner.xml | 124 ++ pom.xml | 12 +- .../controller/{ => jd}/OrderController.java | 2 +- .../business/controller/wx/WXListener.java | 85 + .../java/cn/van/business/enums/EventType.java | 94 + .../java/cn/van/business/enums/FromType.java | 87 + .../java/cn/van/business/enums/IEnum.java | 14 + .../cn/van/business/enums/IEnumForString.java | 14 + .../cn/van/business/enums/JdOrderType.java | 94 + .../cn/van/business/enums/MsgTypeEnum.java | 91 + .../business/enums/ValidCodeConverter.java | 57 + .../java/cn/van/business/enums/WXReqType.java | 129 ++ .../van/business/model/jd/CategoryInfoVO.java | 86 + .../business/model/{ => jd}/GoodsInfoVO.java | 2 +- .../van/business/model/{ => jd}/OrderRow.java | 2 +- .../cn/van/business/model/update-schema.sql | 74 - .../cn/van/business/model/wx/Setting.java | 24 + .../van/business/model/wx/WxEventMessage.java | 59 + .../cn/van/business/model/wx/WxMessage.java | 58 + .../model/wx/WxMessageDataForChat.java | 60 + .../model/wx/WxMessageDataForTransfer.java | 40 + .../java/cn/van/business/model/wx/WxUser.java | 40 + .../repository/OrderRowRepository.java | 6 +- .../repository/SettingRepository.java | 17 + .../WxMessageDataForChatRepository.java | 26 + .../WxMessageDataForTransferRepository.java | 30 + .../business/repository/WxUserRepository.java | 30 + .../java/cn/van/business/util/JDUtils.java | 435 +++-- .../java/cn/van/business/util/QLUtil.java | 353 ++++ src/main/java/cn/van/business/util/Util.java | 1547 +++++++++++++++++ .../java/cn/van/business/util/WXUtil.java | 242 +++ .../van/business/util/WxMessageConsumer.java | 794 +++++++++ src/main/resources/application-dev.yml | 51 + src/main/resources/application.yml | 2 +- 38 files changed, 4628 insertions(+), 185 deletions(-) create mode 100644 .idea/jpa.xml create mode 100644 .idea/uiDesigner.xml rename src/main/java/cn/van/business/controller/{ => jd}/OrderController.java (95%) create mode 100644 src/main/java/cn/van/business/controller/wx/WXListener.java create mode 100644 src/main/java/cn/van/business/enums/EventType.java create mode 100644 src/main/java/cn/van/business/enums/FromType.java create mode 100644 src/main/java/cn/van/business/enums/IEnum.java create mode 100644 src/main/java/cn/van/business/enums/IEnumForString.java create mode 100644 src/main/java/cn/van/business/enums/JdOrderType.java create mode 100644 src/main/java/cn/van/business/enums/MsgTypeEnum.java create mode 100644 src/main/java/cn/van/business/enums/ValidCodeConverter.java create mode 100644 src/main/java/cn/van/business/enums/WXReqType.java create mode 100644 src/main/java/cn/van/business/model/jd/CategoryInfoVO.java rename src/main/java/cn/van/business/model/{ => jd}/GoodsInfoVO.java (98%) rename src/main/java/cn/van/business/model/{ => jd}/OrderRow.java (99%) delete mode 100644 src/main/java/cn/van/business/model/update-schema.sql create mode 100644 src/main/java/cn/van/business/model/wx/Setting.java create mode 100644 src/main/java/cn/van/business/model/wx/WxEventMessage.java create mode 100644 src/main/java/cn/van/business/model/wx/WxMessage.java create mode 100644 src/main/java/cn/van/business/model/wx/WxMessageDataForChat.java create mode 100644 src/main/java/cn/van/business/model/wx/WxMessageDataForTransfer.java create mode 100644 src/main/java/cn/van/business/model/wx/WxUser.java create mode 100644 src/main/java/cn/van/business/repository/SettingRepository.java create mode 100644 src/main/java/cn/van/business/repository/WxMessageDataForChatRepository.java create mode 100644 src/main/java/cn/van/business/repository/WxMessageDataForTransferRepository.java create mode 100644 src/main/java/cn/van/business/repository/WxUserRepository.java create mode 100644 src/main/java/cn/van/business/util/QLUtil.java create mode 100644 src/main/java/cn/van/business/util/Util.java create mode 100644 src/main/java/cn/van/business/util/WXUtil.java create mode 100644 src/main/java/cn/van/business/util/WxMessageConsumer.java create mode 100644 src/main/resources/application-dev.yml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index b63249d..d156338 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,13 +1,12 @@ - + mysql.8 true true - $PROJECT_DIR$/src/main/resources/application.yml com.mysql.cj.jdbc.Driver - jdbc:mysql://134.175.126.60:33306/jd?characterEncoding=utf-8&useSSL=true&serverTimezone=GMT + jdbc:mysql://192.168.8.88:3306/?characterEncoding=utf-8&useSSL=true&serverTimezone=GMT @@ -16,5 +15,17 @@ $ProjectFileDir$ + + redis + true + jdbc.RedisDriver + jdbc:redis://192.168.8.88:6379/7 + + + + + + $ProjectFileDir$ + \ No newline at end of file diff --git a/.idea/jpa.xml b/.idea/jpa.xml new file mode 100644 index 0000000..d281a31 --- /dev/null +++ b/.idea/jpa.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0e5a600..a833e35 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -11,4 +11,7 @@ - \ No newline at end of file + + + diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml index 38b1805..56782ca 100644 --- a/.idea/sqldialects.xml +++ b/.idea/sqldialects.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 79f7680..499900d 100644 --- a/pom.xml +++ b/pom.xml @@ -65,13 +65,16 @@ 1.9.2 - org.codehaus.jackson jackson-core-asl 1.9.2 - + + cn.hutool + hutool-all + 5.8.8 + com.alibaba.fastjson2 @@ -82,6 +85,11 @@ org.springframework.boot spring-boot-starter-data-redis + + org.projectlombok + lombok + provided + diff --git a/src/main/java/cn/van/business/controller/OrderController.java b/src/main/java/cn/van/business/controller/jd/OrderController.java similarity index 95% rename from src/main/java/cn/van/business/controller/OrderController.java rename to src/main/java/cn/van/business/controller/jd/OrderController.java index 3c3d69c..038355c 100644 --- a/src/main/java/cn/van/business/controller/OrderController.java +++ b/src/main/java/cn/van/business/controller/jd/OrderController.java @@ -1,4 +1,4 @@ -package cn.van.business.controller; +package cn.van.business.controller.jd; import cn.van.business.util.JDUtils; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/src/main/java/cn/van/business/controller/wx/WXListener.java b/src/main/java/cn/van/business/controller/wx/WXListener.java new file mode 100644 index 0000000..064e8c1 --- /dev/null +++ b/src/main/java/cn/van/business/controller/wx/WXListener.java @@ -0,0 +1,85 @@ +package cn.van.business.controller.wx; + + +import cn.van.business.model.wx.WxMessage; +import cn.van.business.util.WxMessageConsumer; +import com.alibaba.fastjson2.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/18 0018 下午 05:12V + * @description: + */ +@Controller +@RequestMapping("/wx") +@RestController +public class WXListener { + + private static final Logger logger = LoggerFactory.getLogger(WXListener.class); + + @Autowired + private WxMessageConsumer wxMessageConsumer; + + + + /** + * { + * "event": 10009, + * "wxid": "wxid_kr145nk7l0an31", + * "data": { + * "type": "D0003",* "des": "鏀跺埌娑堟伅", + * "data": { + * "timeStamp": "1702951964728", + * "fromType": 1, + * "msgType": 1, + * "msgSource": 0, + * "fromWxid": "wxid_ytpc72mdoskt22", + * "finalFromWxid": "", + * "atWxidList": [], + * "silence": 0, + * "membercount": 0, + * "signature": "v1_MllZwZMZ", + * "msg": "785", + * "msgBase64": "Nzg1" + * }, + * "timestamp": "1702951964740", + * "wxid": "wxid_kr145nk7l0an31", + * "port": 16888, + * "pid": 10468, + * "flag": "7777" + * } + * } + **/ + @RequestMapping("/message") + public String message(@RequestBody String requestBody) throws Exception { + WxMessage message = JSONObject.parseObject(requestBody, WxMessage.class); + wxMessageConsumer.consume(message); + return "OK"; + } + //@RequestMapping("/test") + //public String test(@RequestBody String requestBody) { + // + // System.out.println("测试接口收到数据:"); + // System.out.println(requestBody); + // return "OK"; + //} + //@RequestMapping("/receive_msg") + //public String receiveMsg(@RequestBody String requestBody) throws Exception { + // + // System.out.println("测试接口收到数据:"); + // System.out.println(requestBody); + // WxMessage message = JSONObject.parseObject(requestBody, WxMessage.class); + // wxMessageConsumer.consume(message); + // + // return "OK"; + //} + +} diff --git a/src/main/java/cn/van/business/enums/EventType.java b/src/main/java/cn/van/business/enums/EventType.java new file mode 100644 index 0000000..d01250b --- /dev/null +++ b/src/main/java/cn/van/business/enums/EventType.java @@ -0,0 +1,94 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:27 + * @description: + */ + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public enum EventType implements IEnum { + // New Events + INJECTED_SUCCESS(10000, "注入成功"), + ACCOUNT_CHANGED(10014, "账号变动事件"), + GROUP_MESSAGE_RECEIVED(10008, "收到群聊消息"), + PRIVATE_MESSAGE_RECEIVED(10009, "收到私聊消息"), + OWN_MESSAGE_SENT(10010, "自己发出消息"), + TRANSFER_EVENT(10006, "转账事件"), + REVOKE_EVENT(10013, "撤回事件"), + FRIEND_REQUEST(10011, "好友请求"), + PAYMENT_EVENT(10007, "支付事件"), + AUTH_EXPIRED(99999, "授权到期"), + QR_CODE_PAYMENT_RECEIVED(10015, "二维码收款事件"), + GROUP_MEMBER_CHANGED(10016, "群成员变动事件"); + private final int key; + + private final String name; + + EventType(int key, String name) { + this.key = key; + this.name = name; + } + + public static EventType get(int key) { + for (EventType e : EventType.values()) { + if (e.getKey() == key) { + return e; + } + } + return null; + } + + public static String getName(Integer key) { + //if (Object.isNotEmpty(key)) { + EventType[] items = EventType.values(); + for (EventType item : items) { + if (item.getKey() == key) { + return item.getName(); + } + } + //} + return ""; + } + + public static Map getKeyValue() { + Map map = new HashMap<>(); + EventType[] items = EventType.values(); + for (EventType item : items) { + map.put(item.getKey() + "", item.getName()); + } + return map; + } + + public static List> getSelectItems() { + List> result = new ArrayList>(); + EventType[] items = EventType.values(); + for (EventType item : items) { + Map map = new HashMap<>(); + map.put("label", item.getName()); + map.put("value", item.getKey()); + result.add(map); + } + return result; + } + + @Override + @JsonValue + public Integer getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + +} + diff --git a/src/main/java/cn/van/business/enums/FromType.java b/src/main/java/cn/van/business/enums/FromType.java new file mode 100644 index 0000000..dd7dfbb --- /dev/null +++ b/src/main/java/cn/van/business/enums/FromType.java @@ -0,0 +1,87 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:27 + * @description: + */ + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public enum FromType implements IEnum { + /** + * fromType 来源类型:1|私聊 2|群聊 3|公众号 + */ + PRIVATE(10009, "私聊"), + GROUP(10008, "群聊"), + MP(3, "公众号"); + private final int key; + + private final String name; + + FromType(int key, String name) { + this.key = key; + this.name = name; + } + + public static FromType get(int key) { + for (FromType e : FromType.values()) { + if (e.getKey() == key) { + return e; + } + } + return null; + } + + public static String getName(Integer key) { + //if (Object.isNotEmpty(key)) { + FromType[] items = FromType.values(); + for (FromType item : items) { + if (item.getKey() == key) { + return item.getName(); + } + } + //} + return ""; + } + + public static Map getKeyVlue() { + Map map = new HashMap<>(); + FromType[] items = FromType.values(); + for (FromType item : items) { + map.put(item.getKey() + "", item.getName()); + } + return map; + } + + public static List> getSelectItems() { + List> result = new ArrayList>(); + FromType[] items = FromType.values(); + for (FromType item : items) { + Map map = new HashMap<>(); + map.put("label", item.getName()); + map.put("value", item.getKey()); + result.add(map); + } + return result; + } + + @Override + @JsonValue + public Integer getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + +} + diff --git a/src/main/java/cn/van/business/enums/IEnum.java b/src/main/java/cn/van/business/enums/IEnum.java new file mode 100644 index 0000000..78b3387 --- /dev/null +++ b/src/main/java/cn/van/business/enums/IEnum.java @@ -0,0 +1,14 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:33 + * @description: + */ +public interface IEnum { + + Integer getKey(); + + String getName(); +} diff --git a/src/main/java/cn/van/business/enums/IEnumForString.java b/src/main/java/cn/van/business/enums/IEnumForString.java new file mode 100644 index 0000000..4e6c46e --- /dev/null +++ b/src/main/java/cn/van/business/enums/IEnumForString.java @@ -0,0 +1,14 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:33 + * @description: + */ +public interface IEnumForString { + + String getKey(); + + String getName(); +} diff --git a/src/main/java/cn/van/business/enums/JdOrderType.java b/src/main/java/cn/van/business/enums/JdOrderType.java new file mode 100644 index 0000000..9d3f2bb --- /dev/null +++ b/src/main/java/cn/van/business/enums/JdOrderType.java @@ -0,0 +1,94 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:27 + * @description: + */ + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public enum JdOrderType implements IEnum { + // New Events + INJECTED_SUCCESS(10000, "注入成功"), + ACCOUNT_CHANGED(10014, "账号变动事件"), + GROUP_MESSAGE_RECEIVED(10008, "收到群聊消息"), + PRIVATE_MESSAGE_RECEIVED(10009, "收到私聊消息"), + OWN_MESSAGE_SENT(10010, "自己发出消息"), + TRANSFER_EVENT(10006, "转账事件"), + REVOKE_EVENT(10013, "撤回事件"), + FRIEND_REQUEST(10011, "好友请求"), + PAYMENT_EVENT(10007, "支付事件"), + AUTH_EXPIRED(99999, "授权到期"), + QR_CODE_PAYMENT_RECEIVED(10015, "二维码收款事件"), + GROUP_MEMBER_CHANGED(10016, "群成员变动事件"); + private final int key; + + private final String name; + + JdOrderType(int key, String name) { + this.key = key; + this.name = name; + } + + public static JdOrderType get(int key) { + for (JdOrderType e : JdOrderType.values()) { + if (e.getKey() == key) { + return e; + } + } + return null; + } + + public static String getName(Integer key) { + //if (Object.isNotEmpty(key)) { + JdOrderType[] items = JdOrderType.values(); + for (JdOrderType item : items) { + if (item.getKey() == key) { + return item.getName(); + } + } + //} + return ""; + } + + public static Map getKeyValue() { + Map map = new HashMap<>(); + JdOrderType[] items = JdOrderType.values(); + for (JdOrderType item : items) { + map.put(item.getKey() + "", item.getName()); + } + return map; + } + + public static List> getSelectItems() { + List> result = new ArrayList>(); + JdOrderType[] items = JdOrderType.values(); + for (JdOrderType item : items) { + Map map = new HashMap<>(); + map.put("label", item.getName()); + map.put("value", item.getKey()); + result.add(map); + } + return result; + } + + @Override + @JsonValue + public Integer getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + +} + diff --git a/src/main/java/cn/van/business/enums/MsgTypeEnum.java b/src/main/java/cn/van/business/enums/MsgTypeEnum.java new file mode 100644 index 0000000..c7cf72d --- /dev/null +++ b/src/main/java/cn/van/business/enums/MsgTypeEnum.java @@ -0,0 +1,91 @@ +package cn.van.business.enums; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 下午 02:31 + * @description: + */ +public enum MsgTypeEnum implements IEnum { + /** + msgType : + 1|文本 3|图片 34|语音 + 42|名片 43|视频 47|动态表情 + 48|地理位置 49|分享链接或附件 2001|红包 + 2002|小程序 2003|群邀请 10000|系统消息 * */ + TEXT(1, "文本"), + IMAGE(3, "图片"), + VOICE(34, "语音"), + ANIMATION(47, "动态表情"), + //名片 42 + CARD(42, "名片"), + + + VIDEO(43, "视频"), + SHARE(49, "分享链接或附件"), + LOCATION(48, "位置"), + REDPACKET(2001, "红包"), + MINIPROGRAM(2002, "小程序"), + GROUP_INVITE(2003, "群邀请"), + SYSTEM(10000, "系统消息"), + ; + private final int key; + + private final String name; + + MsgTypeEnum(int key, String name) { + this.key = key; + this.name = name; + } + + public static String getName(Integer key) { + for (MsgTypeEnum msgTypeEnum : MsgTypeEnum.values()) { + if (msgTypeEnum.key == key) { + return msgTypeEnum.name; + } + } + return null; + } + + public static Map getKeyVlue() { + Map map = new HashMap<>(); + for (MsgTypeEnum msgTypeEnum : MsgTypeEnum.values()) { + map.put(msgTypeEnum.key + "", msgTypeEnum.name); + } + return map; + } + + public static List> getSelectItems() { + List> list = new ArrayList<>(); + for (MsgTypeEnum msgTypeEnum : MsgTypeEnum.values()) { + Map map = new HashMap<>(); + map.put("key", msgTypeEnum.key); + map.put("value", msgTypeEnum.name); + list.add(map); + } + return list; + } + + public static MsgTypeEnum get(int key) { + for (MsgTypeEnum msgTypeEnum : MsgTypeEnum.values()) { + if (msgTypeEnum.key == key) { + return msgTypeEnum; + } + } + return null; + } + + public Integer getKey() { + return key; + } + + public String getName() { + return name; + } + +} diff --git a/src/main/java/cn/van/business/enums/ValidCodeConverter.java b/src/main/java/cn/van/business/enums/ValidCodeConverter.java new file mode 100644 index 0000000..b0b9b46 --- /dev/null +++ b/src/main/java/cn/van/business/enums/ValidCodeConverter.java @@ -0,0 +1,57 @@ +package cn.van.business.enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Leo + * @version 1.0 + * @create 2024/11/9 下午3:08 + * @description: + */ +public class ValidCodeConverter { + private static final Map codeDescriptions = new HashMap<>(); + + static { + codeDescriptions.put(-1, "未知"); + codeDescriptions.put(2, "无效-拆单"); + codeDescriptions.put(3, "无效-取消"); + codeDescriptions.put(4, "无效-京东帮帮主订单"); + codeDescriptions.put(5, "无效-账号异常"); + codeDescriptions.put(6, "无效-赠品类目不返佣"); + codeDescriptions.put(7, "无效-校园订单"); + codeDescriptions.put(8, "无效-企业订单"); + codeDescriptions.put(9, "无效-团购订单"); + codeDescriptions.put(11, "无效-乡村推广员下单"); + codeDescriptions.put(13, "违规订单-其他"); + codeDescriptions.put(14, "无效-来源与备案网址不符"); + codeDescriptions.put(15, "待付款"); + codeDescriptions.put(16, "已付款"); + codeDescriptions.put(17, "已完成(购买用户确认收货)"); + codeDescriptions.put(19, "无效-佣金比例为0"); + codeDescriptions.put(20, "无效-此复购订单对应的首购订单无效"); + codeDescriptions.put(21, "无效-云店订单"); + codeDescriptions.put(22, "无效-PLUS会员佣金比例为0"); + codeDescriptions.put(23, "无效-支付有礼"); + codeDescriptions.put(24, "已付定金"); + codeDescriptions.put(25, "违规订单-流量劫持"); + codeDescriptions.put(26, "违规订单-流量异常"); + codeDescriptions.put(27, "违规订单-违反京东平台规则"); + codeDescriptions.put(28, "违规订单-多笔交易异常"); + codeDescriptions.put(29, "无效-跨屏跨店"); + codeDescriptions.put(30, "无效-累计件数超出类目上限"); + codeDescriptions.put(31, "无效-黑名单sku"); + codeDescriptions.put(33, "超市卡充值订单"); + codeDescriptions.put(34, "无效-推卡订单无效"); + } + + /** + * 获取有效码所对应的描述 + * + * @param code 有效码 + * @return 对应的描述信息 + */ + public String getCodeDescription(Integer code) { + return codeDescriptions.getOrDefault(code, "代码描述未定义"); + } +} diff --git a/src/main/java/cn/van/business/enums/WXReqType.java b/src/main/java/cn/van/business/enums/WXReqType.java new file mode 100644 index 0000000..0f14468 --- /dev/null +++ b/src/main/java/cn/van/business/enums/WXReqType.java @@ -0,0 +1,129 @@ +package cn.van.business.enums; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 10:27 + * @description: + */ + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +import java.util.*; + +public enum WXReqType { + /** + * * 获取微信列表 (X0000) + * * 微信状态检测(Q0000) + * * 发送文本消息(Q0001) + * * 修改下载图片 (Q0002) + * * 获取个人信息 (Q0003) + * * 查询对象信(Q0004) + * * 获取好友列表(Q0005) + * * 获取群聊列表(Q0006) + * * 获取公众号列表(Q0007) + * * 获取群成员列表(Q0008) + * * 发送聊天记录(Q0009) + * * 发送图片 (Q0010) + * * 发送本地文件(Q0011) + * * 发送分享链接(Q0012) + * * 发送小程序(Q0013) + * * 发送音乐分享(Q0014) + * * 发送XML (Q0015) + * * 确认收款 (Q0016) + * * 同意好友请求(Q0017) + * * 添加好友通过v3(Q0018) + * * 添加好友_通过wxid (Q0019) + * * 查询陌生人信息(Q0020) + * * 邀请进群(Q0021) + * * 删除好友(Q0022) + * * 修改对象备注(Q0023) + * * 修改群聊名称(Q0024) + * * 发送名片(Q0025) + */ + GET_WX_LIST("getWeChatList", "获取微信列表"), + GET_WX_STATUS("checkWeChat", "微信状态检测"), + SEND_TEXT_MESSAGE("sendText", "发送文本消息"), + UPDATE_DOWNLOAD_IMAGE("Q0002", "修改下载图片"), + GET_USER_INFO("Q0003", "获取个人信息"), + QUERY_OBJECT_INFO("Q0004", "查询对象信"), + GET_FRIEND_LIST("Q0005", "获取好友列表"), + GET_GROUP_LIST("Q0006", "获取群聊列表"), + GET_MP_LIST("Q0007", "获取公众号列表"), + GET_GROUP_MEMBER_LIST("Q0008", "获取群成员列表"), + SEND_CHAT_RECORD("Q0009", "发送聊天记录"), + SEND_IMAGE("Q0010", "发送图片"), + SEND_LOCAL_FILE("Q0011", "发送本地文件"), + SEND_SHARE_LINK("Q0012", "发送分享链接"), + SEND_MINIPROGRAM("Q0013", "发送小程序"), + SEND_MUSIC_SHARE("Q0014", "发送音乐分享"), + SEND_XML("Q0015", "发送XML"), + CONFIRM_RECEIPT("Q0016", "确认收款"), + AGREE_FRIEND_REQUEST("Q0017", "同意好友请求"), + ADD_FRIEND_V3("Q0018", "添加好友通过v3"), + ADD_FRIEND_WXID("Q0019", "添加好友_通过wxid"), + QUERY_STRANGER_INFO("Q0020", "查询陌生人信息"), + INVITE_GROUP("Q0021", "邀请进群"), + DELETE_FRIEND("Q0022", "删除好友"), + MODIFY_OBJECT_REMARK("Q0023", "修改对象备注"), + MODIFY_GROUP_NAME("Q0024", "修改群聊名称"); + private final String type; + + @Getter + private final String name; + + WXReqType(String type, String name) { + this.type = type; + this.name = name; + } + + public static WXReqType get(String key) { + for (WXReqType e : WXReqType.values()) { + if (Objects.equals(e.getType(), key)) { + return e; + } + } + return null; + } + + public static String getName(String key) { + //if (Object.isNotEmpty(key)) { + WXReqType[] items = WXReqType.values(); + for (WXReqType item : items) { + if (Objects.equals(item.getType(), key)) { + return item.getName(); + } + } + //} + return ""; + } + + public static Map getKeyVlue() { + Map map = new HashMap<>(); + WXReqType[] items = WXReqType.values(); + for (WXReqType item : items) { + map.put(item.getType() + "", item.getName()); + } + return map; + } + + public static List> getSelectItems() { + List> result = new ArrayList>(); + WXReqType[] items = WXReqType.values(); + for (WXReqType item : items) { + Map map = new HashMap<>(); + map.put("label", item.getName()); + map.put("value", item.getType()); + result.add(map); + } + return result; + } + + @JsonValue + public String getType() { + return type; + } + +} + diff --git a/src/main/java/cn/van/business/model/jd/CategoryInfoVO.java b/src/main/java/cn/van/business/model/jd/CategoryInfoVO.java new file mode 100644 index 0000000..d3ab7e6 --- /dev/null +++ b/src/main/java/cn/van/business/model/jd/CategoryInfoVO.java @@ -0,0 +1,86 @@ +package cn.van.business.model.jd; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.io.Serializable; + +/** + * @author Leo + * @version 1.0 + * @create 2024/11/6 下午10:19 + * @description: + */ +public class CategoryInfoVO implements Serializable { + private Long cid1; + private Long cid2; + private Long cid3; + private String cid1Name; + private String cid2Name; + private String cid3Name; + + public CategoryInfoVO() { + } + + @JsonProperty("cid1") + public void setCid1(Long cid1) { + this.cid1 = cid1; + } + + @JsonProperty("cid1") + public Long getCid1() { + return this.cid1; + } + + @JsonProperty("cid2") + public void setCid2(Long cid2) { + this.cid2 = cid2; + } + + @JsonProperty("cid2") + public Long getCid2() { + return this.cid2; + } + + @JsonProperty("cid3") + public void setCid3(Long cid3) { + this.cid3 = cid3; + } + + @JsonProperty("cid3") + public Long getCid3() { + return this.cid3; + } + + @JsonProperty("cid1Name") + public void setCid1Name(String cid1Name) { + this.cid1Name = cid1Name; + } + + @JsonProperty("cid1Name") + public String getCid1Name() { + return this.cid1Name; + } + + @JsonProperty("cid2Name") + public void setCid2Name(String cid2Name) { + this.cid2Name = cid2Name; + } + + @JsonProperty("cid2Name") + public String getCid2Name() { + return this.cid2Name; + } + + @JsonProperty("cid3Name") + public void setCid3Name(String cid3Name) { + this.cid3Name = cid3Name; + } + + @JsonProperty("cid3Name") + public String getCid3Name() { + return this.cid3Name; + } +} + diff --git a/src/main/java/cn/van/business/model/GoodsInfoVO.java b/src/main/java/cn/van/business/model/jd/GoodsInfoVO.java similarity index 98% rename from src/main/java/cn/van/business/model/GoodsInfoVO.java rename to src/main/java/cn/van/business/model/jd/GoodsInfoVO.java index 9e983a6..2ea06f0 100644 --- a/src/main/java/cn/van/business/model/GoodsInfoVO.java +++ b/src/main/java/cn/van/business/model/jd/GoodsInfoVO.java @@ -1,4 +1,4 @@ -package cn.van.business.model; +package cn.van.business.model.jd; import javax.persistence.*; diff --git a/src/main/java/cn/van/business/model/OrderRow.java b/src/main/java/cn/van/business/model/jd/OrderRow.java similarity index 99% rename from src/main/java/cn/van/business/model/OrderRow.java rename to src/main/java/cn/van/business/model/jd/OrderRow.java index efd6fce..92a89a1 100644 --- a/src/main/java/cn/van/business/model/OrderRow.java +++ b/src/main/java/cn/van/business/model/jd/OrderRow.java @@ -1,4 +1,4 @@ -package cn.van.business.model; +package cn.van.business.model.jd; /** * @author Leo diff --git a/src/main/java/cn/van/business/model/update-schema.sql b/src/main/java/cn/van/business/model/update-schema.sql deleted file mode 100644 index 9fcdedc..0000000 --- a/src/main/java/cn/van/business/model/update-schema.sql +++ /dev/null @@ -1,74 +0,0 @@ -CREATE TABLE goods_info -( - id BIGINT AUTO_INCREMENT NOT NULL, - owner VARCHAR(255) NULL, - main_sku_id VARCHAR(255) NULL, - product_id VARCHAR(255) NULL, - image_url VARCHAR(255) NULL, - shop_name VARCHAR(255) NULL, - shop_id VARCHAR(255) NULL, - CONSTRAINT pk_goods_info PRIMARY KEY (id) -); - -CREATE TABLE order_rows -( - id VARCHAR(255) NOT NULL, - order_id BIGINT NULL, - parent_id BIGINT NULL, - order_time datetime NULL, - finish_time datetime NULL, - modify_time datetime NULL, - order_emt INT NULL, - plus INT NULL, - union_id BIGINT NULL, - sku_id BIGINT NULL, - sku_name VARCHAR(255) NULL, - sku_num INT NULL, - sku_return_num INT NULL, - sku_frozen_num INT NULL, - price DOUBLE NULL, - commission_rate DOUBLE NULL, - sub_side_rate DOUBLE NULL, - subsidy_rate DOUBLE NULL, - final_rate DOUBLE NULL, - estimate_cos_price DOUBLE NULL, - estimate_fee DOUBLE NULL, - actual_cos_price DOUBLE NULL, - actual_fee DOUBLE NULL, - valid_code INT NULL, - trace_type INT NULL, - position_id BIGINT NULL, - site_id BIGINT NULL, - union_alias VARCHAR(255) NULL, - pid VARCHAR(255) NULL, - cid1 BIGINT NULL, - cid2 BIGINT NULL, - cid3 BIGINT NULL, - sub_union_id VARCHAR(255) NULL, - union_tag VARCHAR(255) NULL, - pop_id BIGINT NULL, - ext1 VARCHAR(255) NULL, - pay_month VARCHAR(255) NULL, - cp_act_id BIGINT NULL, - union_role INT NULL, - gift_coupon_ocs_amount DOUBLE NULL, - gift_coupon_key VARCHAR(255) NULL, - balance_ext VARCHAR(255) NULL, - sign VARCHAR(255) NULL, - pro_price_amount DOUBLE NULL, - rid BIGINT NULL, - goods_info_id BIGINT NULL, - express_status INT NULL, - channel_id BIGINT NULL, - sku_tag VARCHAR(255) NULL, - item_id VARCHAR(255) NULL, - caller_item_id VARCHAR(255) NULL, - order_tag VARCHAR(255) NULL, - CONSTRAINT pk_order_rows PRIMARY KEY (id) -); - -ALTER TABLE order_rows - ADD CONSTRAINT uc_order_rows_goods_info UNIQUE (goods_info_id); - -ALTER TABLE order_rows - ADD CONSTRAINT FK_ORDER_ROWS_ON_GOODS_INFO FOREIGN KEY (goods_info_id) REFERENCES goods_info (id); \ No newline at end of file diff --git a/src/main/java/cn/van/business/model/wx/Setting.java b/src/main/java/cn/van/business/model/wx/Setting.java new file mode 100644 index 0000000..15f456c --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/Setting.java @@ -0,0 +1,24 @@ +package cn.van.business.model.wx; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@Table(name = "setting") +public class Setting { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Integer id; + + @Column(name = "setting_key", nullable = false) + private String settingKey; + + @Column(name = "setting_value") + private String settingValue; + +} diff --git a/src/main/java/cn/van/business/model/wx/WxEventMessage.java b/src/main/java/cn/van/business/model/wx/WxEventMessage.java new file mode 100644 index 0000000..92a50ed --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/WxEventMessage.java @@ -0,0 +1,59 @@ +package cn.van.business.model.wx; + +/** + * @author Leo + * @version 1.0 + * @create 2024/11/8 下午10:58 + * @description: + */ + + +import lombok.Data; + +import java.util.List; + +/** + * Main class representing the structure of the received WX message. + */ +@Data +public class WxEventMessage { + + private Integer event; + private String wxid; + private MessageData data; + + /** + * Inner class to encapsulate the data section of the message. + */ + @Data + public static class MessageData { + private String type; + private String des; + private EventData data; + private Long timestamp; + private String wxid; + private Integer port; + private Integer pid; + private String flag; + + /** + * Inner class to encapsulate the nested data section within the data. + */ + @Data + public static class EventData { + private Long timeStamp; + private Integer fromType; + private Integer msgType; + private Integer msgSource; + private String fromWxid; + private String finalFromWxid; + private List atWxidList; // List to store the WX IDs in the atWxidList array. + private Integer silence; + private Integer membercount; + private String signature; + private String msg; + private String msgId; + private String msgBase64; + } + } +} diff --git a/src/main/java/cn/van/business/model/wx/WxMessage.java b/src/main/java/cn/van/business/model/wx/WxMessage.java new file mode 100644 index 0000000..6dfcd86 --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/WxMessage.java @@ -0,0 +1,58 @@ +package cn.van.business.model.wx; + +/** + * @author Leo + * @version 1.0 + * @create 2024/11/9 上午12:06 + * @description: + */ + +import lombok.Data; + +import java.util.List; + +/** + * Entity class representing the structure of the WX message event. + */ +@Data +public class WxMessage { + + private Integer event; + private String wxid; + private DataSection data; + + /** + * Nested class to hold the data section of the WX message. + */ + @Data + public static class DataSection { + private String type; + private String des; + private InnerData data; + private Long timestamp; + private String wxid; + private Integer port; + private Integer pid; + private String flag; + + /** + * Nested class for the innermost data structure. + */ + @Data + public static class InnerData { + private Long timeStamp; + private Integer fromType; + private Integer msgType; + private Integer msgSource; + private String fromWxid; + private String finalFromWxid; + private List atWxidList; + private Integer silence; + private Integer membercount; + private String signature; + private String msg; + private String msgId; + private String msgBase64; + } + } +} diff --git a/src/main/java/cn/van/business/model/wx/WxMessageDataForChat.java b/src/main/java/cn/van/business/model/wx/WxMessageDataForChat.java new file mode 100644 index 0000000..16c3698 --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/WxMessageDataForChat.java @@ -0,0 +1,60 @@ +package cn.van.business.model.wx; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@Table(name = "wx_message_data_for_chat") +public class WxMessageDataForChat { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Integer id; + + @Column(name = "timeStamp") + private Long timeStamp; + + @Column(name = "fromType") + private Integer fromType; + + @Column(name = "msgType") + private Integer msgType; + + @Column(name = "msgSource") + private Integer msgSource; + + @Column(name = "fromWxid") + private String fromWxid; + + @Column(name = "finalFromWxid") + private String finalFromWxid; + + @Column(name = "atWxidList") + private String atWxidList; + + @Column(name = "silence") + private Integer silence; + + @Column(name = "membercount") + private Integer membercount; + + @Column(name = "signature") + private String signature; + + @Column(name = "msg", length = 1000) + private String msg; + + @Column(name = "msgBase64", length = 1000) + private String msgBase64; + + @Column(name = "msgId") + private String msgId; + + @Column(name = "msgTypeStr") + private String msgTypeStr; + +} diff --git a/src/main/java/cn/van/business/model/wx/WxMessageDataForTransfer.java b/src/main/java/cn/van/business/model/wx/WxMessageDataForTransfer.java new file mode 100644 index 0000000..77dffc7 --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/WxMessageDataForTransfer.java @@ -0,0 +1,40 @@ +package cn.van.business.model.wx; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.math.BigDecimal; + +@Getter +@Setter +@Entity +@Table(name = "wx_message_data_for_transfer") +public class WxMessageDataForTransfer { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Integer id; + + @Column(name = "fromWxid", nullable = false) + private String fromWxid; + + @Column(name = "msgSource") + private Integer msgSource; + + @Column(name = "transType") + private Integer transType; + + @Column(name = "money", precision = 10, scale = 2) + private BigDecimal money; + + @Column(name = "memo") + private String memo; + + @Column(name = "transferid", nullable = false) + private String transferid; + + @Column(name = "invalidtime") + private Integer invalidtime; + +} diff --git a/src/main/java/cn/van/business/model/wx/WxUser.java b/src/main/java/cn/van/business/model/wx/WxUser.java new file mode 100644 index 0000000..2d83a62 --- /dev/null +++ b/src/main/java/cn/van/business/model/wx/WxUser.java @@ -0,0 +1,40 @@ +package cn.van.business.model.wx; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.math.BigDecimal; + +@Getter +@Setter +@Entity +@Table(name = "wx_user") +public class WxUser { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Integer id; + + @Column(name = "wxid", nullable = false) + private String wxid; + + @Column(name = "money_leiji", precision = 10, scale = 2) + private BigDecimal moneyLeiji; + + @Column(name = "money_shengyu", precision = 10, scale = 2) + private BigDecimal moneyShengyu; + + @Column(name = "count_chongzhi", precision = 10, scale = 2) + private BigDecimal countChongzhi; + + @Column(name = "count_xiaofei", precision = 10, scale = 2) + private BigDecimal countXiaofei; + + @Column(name = "name") + private String name; + + @Column(name = "status") + private Integer status; + +} diff --git a/src/main/java/cn/van/business/repository/OrderRowRepository.java b/src/main/java/cn/van/business/repository/OrderRowRepository.java index c15b3e2..3d9f887 100644 --- a/src/main/java/cn/van/business/repository/OrderRowRepository.java +++ b/src/main/java/cn/van/business/repository/OrderRowRepository.java @@ -7,7 +7,7 @@ package cn.van.business.repository; * @description: */ -import cn.van.business.model.OrderRow; +import cn.van.business.model.jd.OrderRow; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -25,4 +25,8 @@ public interface OrderRowRepository extends JpaRepository { // 根据有效码查询订单行 List findByValidCode(int validCode); + + // 查找 validCode != 15 或者 !=-1 的订单行 ,并且按orderTime 降序 + List findByValidCodeNotInOrderByOrderTimeDesc(int[] validCodes); + } diff --git a/src/main/java/cn/van/business/repository/SettingRepository.java b/src/main/java/cn/van/business/repository/SettingRepository.java new file mode 100644 index 0000000..d67dba2 --- /dev/null +++ b/src/main/java/cn/van/business/repository/SettingRepository.java @@ -0,0 +1,17 @@ +package cn.van.business.repository; + +import cn.van.business.model.wx.Setting; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface SettingRepository extends JpaRepository { + + // Find a setting by its unique key + Optional findBySettingKey(String settingKey); + + // You could add more queries if needed, example: + // List findBySettingKeyStartingWith(String prefix); +} diff --git a/src/main/java/cn/van/business/repository/WxMessageDataForChatRepository.java b/src/main/java/cn/van/business/repository/WxMessageDataForChatRepository.java new file mode 100644 index 0000000..bb829f0 --- /dev/null +++ b/src/main/java/cn/van/business/repository/WxMessageDataForChatRepository.java @@ -0,0 +1,26 @@ +package cn.van.business.repository; + +import cn.van.business.model.wx.WxMessageDataForChat; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface WxMessageDataForChatRepository extends JpaRepository { + // 根据消息来源wxid查询消息记录 + List findByFromWxid(String fromWxid); + + // 根据消息类型查询消息记录 + List findByMsgType(Integer msgType); + + // 根据消息ID查询单条消息记录 + WxMessageDataForChat findByMsgId(String msgId); + + // 添加其他可能需要的自定义查询方法,例如: + // 根据消息类型和是否是静默消息查询 + List findByMsgTypeAndSilence(Integer msgType, Integer silence); + + // 根据消息来源和消息类型查询 + List findByFromWxidAndMsgType(String fromWxid, Integer msgType); +} diff --git a/src/main/java/cn/van/business/repository/WxMessageDataForTransferRepository.java b/src/main/java/cn/van/business/repository/WxMessageDataForTransferRepository.java new file mode 100644 index 0000000..6da4a18 --- /dev/null +++ b/src/main/java/cn/van/business/repository/WxMessageDataForTransferRepository.java @@ -0,0 +1,30 @@ +package cn.van.business.repository; + +import cn.van.business.model.wx.WxMessageDataForTransfer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.util.List; + +@Repository +public interface WxMessageDataForTransferRepository extends JpaRepository { + + // Find transfers by the sender's WeChat ID + List findByFromWxid(String fromWxid); + + // Find a single transfer by its unique transfer ID + WxMessageDataForTransfer findByTransferid(String transferid); + + // Find transfers by transaction type + List findByTransType(Integer transType); + + // Find transfers that have a specific amount of money + List findByMoney(BigDecimal money); + + // Optional: Find transfers by msgSource, which could represent different sources or contexts of the transactions + List findByMsgSource(Integer msgSource); + + // Optional: Find transfers by their invalid time parameter + List findByInvalidtime(Integer invalidtime); +} diff --git a/src/main/java/cn/van/business/repository/WxUserRepository.java b/src/main/java/cn/van/business/repository/WxUserRepository.java new file mode 100644 index 0000000..dfda87f --- /dev/null +++ b/src/main/java/cn/van/business/repository/WxUserRepository.java @@ -0,0 +1,30 @@ +package cn.van.business.repository; + +import cn.van.business.model.wx.WxUser; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +@Repository +public interface WxUserRepository extends JpaRepository { + + // Find users by their wxid + Optional findByWxid(String wxid); + + // Find users by their name + List findByName(String name); + + // Find users by status + List findByStatus(Integer status); + + // Optional: Find users by the range of total accumulated money + List findByMoneyLeijiBetween(BigDecimal min, BigDecimal max); + + // Optional: Find users by the remaining money + List findByMoneyShengyuLessThanEqual(BigDecimal amount); + + // Add more customized queries as needed based on your application requirements +} diff --git a/src/main/java/cn/van/business/util/JDUtils.java b/src/main/java/cn/van/business/util/JDUtils.java index 6d7b29f..b75252a 100644 --- a/src/main/java/cn/van/business/util/JDUtils.java +++ b/src/main/java/cn/van/business/util/JDUtils.java @@ -1,17 +1,22 @@ package cn.van.business.util; -import cn.van.business.model.GoodsInfoVO; -import cn.van.business.model.OrderRow; +import cn.van.business.enums.ValidCodeConverter; +import cn.van.business.model.jd.GoodsInfoVO; +import cn.van.business.model.jd.OrderRow; import cn.van.business.repository.OrderRowRepository; +import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.util.DateUtils; import com.jd.open.api.sdk.DefaultJdClient; import com.jd.open.api.sdk.JdClient; import com.jd.open.api.sdk.domain.kplunion.OrderService.request.query.OrderRowReq; import com.jd.open.api.sdk.domain.kplunion.OrderService.response.query.GoodsInfo; import com.jd.open.api.sdk.domain.kplunion.OrderService.response.query.OrderRowResp; +import com.jd.open.api.sdk.domain.kplunion.promotioncommon.PromotionService.request.get.PromotionCodeReq; import com.jd.open.api.sdk.request.kplunion.UnionOpenOrderRowQueryRequest; +import com.jd.open.api.sdk.request.kplunion.UnionOpenPromotionCommonGetRequest; import com.jd.open.api.sdk.response.kplunion.UnionOpenOrderRowQueryResponse; +import com.jd.open.api.sdk.response.kplunion.UnionOpenPromotionCommonGetResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.StringRedisTemplate; @@ -21,9 +26,12 @@ import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import static java.lang.Thread.sleep; @@ -37,109 +45,31 @@ import static java.lang.Thread.sleep; public class JDUtils { private static final String SERVER_URL = "https://api.jd.com/routerjson"; + // van论坛 private static final String APP_KEY = "98e21c89ae5610240ec3f5f575f86a59"; private static final String SECRET_KEY = "3dcb6b23a1104639ac433fd07adb6dfb"; + // 导购的 + //private static final String APP_KEY = "faf410cb9587dc80dc7b31e321d7d322"; + //private static final String SECRET_KEY = + // "a4fb15d7bedd4316b97b4e96e4effc1c"; + //accessToken + private static final String ACCESS_TOKEN = ""; + //标记唯一订单行:订单+sku维度的唯一标识 + private static final String ORDER_ROW_KEY = "jd:order:row:"; + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Autowired private StringRedisTemplate redisTemplate; @Autowired private OrderRowRepository orderRowRepository; - - private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - - /** - * 拉取最新的订单 - */ - @Scheduled(cron = "0 * * * * ?") // 每分钟执行一次 - public void fetchLatestOrder() throws Exception { - LocalDateTime now = LocalDateTime.now(); - LocalDateTime lastMinute = now.minusMinutes(1).withSecond(0).withNano(0); - - UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(lastMinute, true); // 真实代表实时订单 - if (response != null){ - int code = response.getQueryResult().getCode(); - if (code == 200) { - if (response.getQueryResult().getCode() == 200) { - OrderRowResp[] orderRowResps = response.getQueryResult().getData(); - if (orderRowResps == null) { - return; - } - for (OrderRowResp orderRowResp : orderRowResps) { - // 固化到数据库 - OrderRow orderRow = createOrderRow(orderRowResp); - // 订单号不存在就保存,存在就更新订单状态 - orderRowRepository.save(orderRow); - } - } - } - } - - } - - /** - * 拉取历史订单 - */ - public void fetchHistoricalOrders() throws Exception { - // 从设定的开始日期到昨天的同一时间 - LocalDateTime startDate = LocalDateTime.of(2024, 7, 1, 0, 0); - LocalDateTime now = LocalDateTime.now(); - LocalDateTime lastHour = now.minusHours(1).withMinute(0).withSecond(0).withNano(0); - - - while (!startDate.isEqual(lastHour)) { - UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(startDate, false); // 假的代表历史订单 - - if (response != null){ - int code = response.getQueryResult().getCode(); - if (code == 200) { - if (response.getQueryResult().getCode() == 200) { - OrderRowResp[] orderRowResps = response.getQueryResult().getData(); - if (orderRowResps == null) { - continue; - } - for (OrderRowResp orderRowResp : orderRowResps) { - // 固化到数据库 - OrderRow orderRow = createOrderRow(orderRowResp); - // 订单号不存在就保存,存在就更新订单状态 - orderRowRepository.save(orderRow); - } - } - } - sleep(1000); - } - startDate = startDate.plusHours(1); - - } - } - - /** - * 根据指定的日期时间拉取订单 - */ - public UnionOpenOrderRowQueryResponse fetchOrdersForDateTime(LocalDateTime dateTime, boolean isRealTime) throws Exception { - LocalDateTime endTime = isRealTime ? dateTime.plusMinutes(1) : dateTime.plusHours(1); - String key = dateTime.format(DATE_TIME_FORMATTER); - String hourRange = isRealTime ? "minute" : "hour"; - - SetOperations setOps = redisTemplate.opsForSet(); - - // 检查是否标记为已拉取 - if (Boolean.TRUE.equals(setOps.isMember(key, hourRange))) { - System.out.println(dateTime.format(DATE_TIME_FORMATTER) + " 已经拉取,跳过"); - return null; - } - - // 调用 API 以拉取订单 - UnionOpenOrderRowQueryResponse unionOpenOrderRowQueryResponse = getUnionOpenOrderRowQueryResponse(dateTime, endTime); - - // 标记已拉取 - setOps.add(key, hourRange); - return unionOpenOrderRowQueryResponse; - } + @Autowired + private WXUtil wxUtil; /** * 将 响应参数转化为 OrderRow,并返回 - * */ + */ private static OrderRow createOrderRow(OrderRowResp orderRowResp) { OrderRow orderRow = new OrderRow(); orderRow.setOrderId(orderRowResp.getOrderId()); @@ -212,6 +142,240 @@ public class JDUtils { return orderRow; } + private static List filterOrdersByDate(List orderRows, int daysBack) { + LocalDate now = LocalDate.now(); + + return orderRows.stream() + .filter(order -> { + // 将 Date 转换为 LocalDate + LocalDate orderDate = order.getOrderTime().toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + + // 计算是否在给定的天数内 + return !orderDate.isBefore(now.minusDays(daysBack)) && !orderDate.isAfter(now); + }) + .collect(Collectors.toList()); + } + + /** + * 拉取最新的订单 1440分钟 + */ + @Scheduled(cron = "0 * * * * ?") // 每分钟执行一次 + public void fetchLatestOrder() throws Exception { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastMinute = now.minusMinutes(10).withSecond(0).withNano(0); + + UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(lastMinute, true); // 真实代表实时订单 + if (response != null) { + + int code = response.getQueryResult().getCode(); + if (code == 200) { + if (response.getQueryResult().getCode() == 200) { + OrderRowResp[] orderRowResps = response.getQueryResult().getData(); + if (orderRowResps == null) { + return; + } + for (OrderRowResp orderRowResp : orderRowResps) { + // 固化到数据库 + OrderRow orderRow = createOrderRow(orderRowResp); + // 订单号不存在就保存,存在就更新订单状态 + orderRowRepository.save(orderRow); + } + } + } + } + + } + + @Scheduled(cron = "0 * * * * ?") // 每分钟执行一次 + public void sendOrderToWx() { + int[] parm = {-1, 17}; + List orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDesc(parm); + if (!orderRows.isEmpty()) { + for (OrderRow orderRow : orderRows) { + orderToWx(orderRow, true); + } + } + + } + + /** + * 指令 + */ + public void sendOrderToWxByOrder(String order) { + int[] parm = {-1}; + List orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDesc(parm); + /** + * 菜单: + * 今日统计 + * 昨天统计 + * 最近七天统计 + * 最近一个月统计 + * 今天订单 + * 昨天订单 + * */ + String content = null; + switch (order) { + case "菜单": + content = "菜单:京粉 +命令 \n 如: 京粉 今日统计\r"; + content += "今日统计\r"; + content += "昨天统计\r"; + content += "最近七天统计\r"; + content += "最近一个月统计\r"; + content += "今天订单\r"; + content += "昨天订单\r"; + break; + case "今日统计": { + List todayOrders = filterOrdersByDate(orderRows, 0); + // 订单总数,已付款,已取消,佣金总计 + content = "今日统计:"; + content += "订单总数:" + todayOrders.size() + "\r"; + content += "已付款:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + + content += "佣金总计:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + break; + } + case "昨天统计": { + List yesterdayOrders = filterOrdersByDate(orderRows, 1); + content = "昨天统计:"; + content += "订单总数:" + yesterdayOrders.size() + "\r"; + content += "已付款:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + content += "佣金总计:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + break; + } + case "最近七天统计": + List last7DaysOrders = filterOrdersByDate(orderRows, 7); + content = "最近七天统计:"; + content += "订单总数:" + last7DaysOrders.size() + "\r"; + content += "已付款:" + last7DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + last7DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + content += "佣金总计:" + last7DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + break; + case "最近一个月统计": + List last30DaysOrders = filterOrdersByDate(orderRows, 30); + content = "最近一个月统计:"; + content += "订单总数:" + last30DaysOrders.size() + "\r"; + content += "已付款:" + last30DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + last30DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + content += "佣金总计:" + last30DaysOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + break; + case "今天订单": { + List todayOrders = filterOrdersByDate(orderRows, 0); + // 订单总数,已付款,已取消,佣金总计 + content = "今日统计:"; + content += "订单总数:" + todayOrders.size() + "\r"; + content += "已付款:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + + content += "佣金总计:" + todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + for (OrderRow orderRow : todayOrders) { + orderToWx(orderRow, false); + } + + break; + } + case "昨天订单": { + List yesterdayOrders = filterOrdersByDate(orderRows, 1); + content = "昨天统计:"; + content += "订单总数:" + yesterdayOrders.size() + "\r"; + content += "已付款:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).count() + "\r"; + content += "已取消:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() != 16).count() + "\r"; + content += "佣金总计:" + yesterdayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(); + + for (OrderRow orderRow : yesterdayOrders) { + orderToWx(orderRow, false); + } + break; + } + } + if (content != null) { + wxUtil.sendTextMessage(WXUtil.super_admin_wxid, content, 1, WXUtil.super_admin_wxid); + } + + } + + private void orderToWx(OrderRow orderRow, Boolean isAutoFlush) { + // 查询订单状态 + Integer newValidCode = orderRow.getValidCode(); + String oldValidCode = redisTemplate.opsForValue().get(ORDER_ROW_KEY + orderRow.getId()); + Integer lastValidCode = 0; + // 更新 Redis 状态 + redisTemplate.opsForValue().set(ORDER_ROW_KEY + orderRow.getId(), String.valueOf(orderRow.getValidCode())); + + if (Util.isNotEmpty(oldValidCode)) { + lastValidCode = Integer.valueOf(oldValidCode); + } + // 如果订单状态没变化,就不发送 + if (isAutoFlush && lastValidCode.equals(newValidCode)) { + } else { + String content; + content = getFormattedOrderInfo(orderRow); + + // 推送 + wxUtil.sendTextMessage(WXUtil.super_admin_wxid, content, 1, WXUtil.super_admin_wxid); + try { + sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + public String getFormattedOrderInfo(OrderRow orderRow) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + ValidCodeConverter converter = new ValidCodeConverter(); + String orderInfo = + "商品名称:" + orderRow.getSkuName() + "\r" + + "订单+sku:" + orderRow.getId() + "\r" + + "订单号:" + orderRow.getOrderId() + "(" + (orderRow.getPlus() == 1 ? "plus" : "非plus") + ")\r" + + "佣金比例:" + orderRow.getCommissionRate() + "%\r\r" + + "商品单价:" + orderRow.getPrice() + "\r" + + "商品数量:" + orderRow.getSkuNum() + "\r" + + "商品总价:" + (orderRow.getPrice() * orderRow.getSkuNum()) + "\r" + + "订单总价:" + (orderRow.getActualCosPrice() * orderRow.getSkuNum()) + "\r" + + "预估计佣金额:" + orderRow.getEstimateCosPrice() + "\n" + + "推客的预估佣金:" + orderRow.getEstimateFee() + "\r" + + "实际计算佣金的金额:" + orderRow.getActualCosPrice() + "\r" + + "下单时间:" + formatter.format(orderRow.getOrderTime()) + "\r" + + "完成时间:" + (orderRow.getFinishTime() != null ? formatter.format(orderRow.getFinishTime()) : "未完成") + "\r\n" + + "订单状态:" + (converter.getCodeDescription(orderRow.getValidCode())) + "\r"; + + return orderInfo + "祝 财源滚滚"; + } + + /** + * 根据指定的日期时间拉取订单 + */ + public UnionOpenOrderRowQueryResponse fetchOrdersForDateTime(LocalDateTime dateTime, boolean isRealTime) throws Exception { + LocalDateTime endTime = isRealTime ? dateTime.plusMinutes(10) : dateTime.plusHours(1); + String key = dateTime.format(DATE_TIME_FORMATTER); + String hourRange = isRealTime ? "minute" : "hour"; + + SetOperations setOps = redisTemplate.opsForSet(); + + // 检查是否标记为已拉取 + //if (Boolean.TRUE.equals(setOps.isMember(key, hourRange))) { + // System.out.println(dateTime.format(DATE_TIME_FORMATTER) + " 已经拉取,跳过"); + // return null; + //} + + // 调用 API 以拉取订单 + UnionOpenOrderRowQueryResponse unionOpenOrderRowQueryResponse = getUnionOpenOrderRowQueryResponse(dateTime, endTime); + + // 标记已拉取 + setOps.add(key, hourRange); + return unionOpenOrderRowQueryResponse; + } + /** * 获取订单列表 * @@ -224,11 +388,10 @@ public class JDUtils { String startTime = start.format(DATE_TIME_FORMATTER); String endTime = end.format(DATE_TIME_FORMATTER); // 模拟 API 调用 - System.out.println("调用API - 从 " + startTime - + " 到 " + endTime); + //System.out.println("调用API - 从 " + startTime + // + " 到 " + endTime); // 实际的 API 调用逻辑应在这里进行 - String accessToken = ""; - JdClient client = new DefaultJdClient(SERVER_URL, accessToken, APP_KEY, SECRET_KEY); + JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, APP_KEY, SECRET_KEY); UnionOpenOrderRowQueryRequest request = new UnionOpenOrderRowQueryRequest(); OrderRowReq orderReq = new OrderRowReq(); orderReq.setPageIndex(1); @@ -250,4 +413,78 @@ public class JDUtils { return client.execute(request); } + + /** + * 转链 + */ + String transfer(String url) throws Exception { + JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, APP_KEY, SECRET_KEY); + UnionOpenPromotionCommonGetRequest request = new UnionOpenPromotionCommonGetRequest(); + request.setVersion("1.0"); + request.setSignmethod("md5"); + + PromotionCodeReq promotionCodeReq = new PromotionCodeReq(); + promotionCodeReq.setMaterialId(url); + promotionCodeReq.setSiteId( + "4101253066"); + promotionCodeReq.setSceneId(1); + promotionCodeReq.setCommand(1); + promotionCodeReq.setProType(5); + + + request.setPromotionCodeReq(promotionCodeReq); + UnionOpenPromotionCommonGetResponse response = client.execute(request); + + String jsonString = JSON.toJSONString(response); + System.out.println(jsonString); + // + //System.out.println(request.getAppJsonParams()); + //System.out.println(request.getPromotionCodeReq()); + // + //System.out.println("--------"); + //System.out.println(response.getGetResult().getCode()); + //System.out.println(response.getGetResult().getMessage()); + //System.out.println(response.getGetResult().getData().getClickURL()); + //System.out.println(response.getGetResult().getData().getJCommand()); + return response.getGetResult().getData().getClickURL(); + } + + // 拉取历史订单 2880 次请求 + @Scheduled(cron = "0 0 8,23 * * ?") + public void fetchHistoricalOrders() throws Exception { + // 从设定的开始日期到昨天的同一时间 + System.out.println("开始拉取历史订单"); + // 拉最近两个月的订单 + // 获取当前时间,并调整为整点开始 + LocalDateTime startDate = LocalDateTime.now().minusMonths(2).truncatedTo(ChronoUnit.HOURS); + + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastHour = now.minusHours(1).withMinute(0).withSecond(0).withNano(0); + + + while (!startDate.isEqual(lastHour)) { + UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(startDate, false); // 假的代表历史订单 + + if (response != null) { + int code = response.getQueryResult().getCode(); + if (code == 200) { + if (response.getQueryResult().getCode() == 200) { + OrderRowResp[] orderRowResps = response.getQueryResult().getData(); + if (orderRowResps == null) { + continue; + } + for (OrderRowResp orderRowResp : orderRowResps) { + // 固化到数据库 + OrderRow orderRow = createOrderRow(orderRowResp); + // 订单号不存在就保存,存在就更新订单状态 + orderRowRepository.save(orderRow); + sleep(1000); + } + } + } + } + startDate = startDate.plusHours(1); + + } + } } diff --git a/src/main/java/cn/van/business/util/QLUtil.java b/src/main/java/cn/van/business/util/QLUtil.java new file mode 100644 index 0000000..0c25cba --- /dev/null +++ b/src/main/java/cn/van/business/util/QLUtil.java @@ -0,0 +1,353 @@ +package cn.van.business.util; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static cn.hutool.core.thread.ThreadUtil.sleep; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/22 0022 上午 09:59 + * @description: + */ +@Component +public class QLUtil { + //client_id + public static final String CLIENT_ID = "Ouv_S9gk5LpV"; + //client_secret + public static final String CLIENT_SECRET = "1pLjAIfBBzu1_UA9q-hOj778"; + public static final String QL_TOKEN_KEY = "QL_TOKEN_KEY"; + //private static final RedisCache redisCache = SpringUtil.getBean(RedisCache.class); + private static final Logger logger = LoggerFactory.getLogger(QLUtil.class); + /** + * 1. 在系统设置 -> 应用设置 -> 添加应用,权限目前支持5个模块,可以选择多个模块。选择一个模块之后,可读写此模块的所有接口。 + * 2. 使用生成的 client_id 和 client_secret 请求获取token接口 http://localhost:5700/open/auth/token?client_id=xxxxxx&client_secret=xxxxxxxx + * 3. 上面接口返回的token有效期为30天,可用于请求青龙的接口 curl 'http://localhost:5700/open/envs?searchValue=&t=1630032278171' -H 'Authorization: Bearer + * 接口返回的token' + * 4. openapi的接口与系统正常接口的区别就是青龙里的是/api/envs,openapi是/open/envs,即就是青龙接口中的api换成open + */ + public static String QL_BASE_URL = "http://134.175.126.60:45700"; + public static final String GET_TOKEN = QL_BASE_URL + "/open/auth/token"; + // /open/envs + public static final String GET_ENV = QL_BASE_URL + "/open/envs"; + // /open/crons + public static final String GET_CRON = QL_BASE_URL + "/open/crons"; + private Environment env; + @Autowired + private StringRedisTemplate redisTemplate; + + public QLUtil(Environment env) { + this.env = env; + QL_BASE_URL = env.getProperty("config.QL_BASE_URL"); + } + + public String getToken() { + + String token = null; + token = redisTemplate.opsForValue().get(QL_TOKEN_KEY); + if (StrUtil.isNotEmpty(token)) { + + } else { + //HashMap map = new HashMap<>(); + //map.put("client_id", CLIENT_ID); + //map.put("client_secret", CLIENT_SECRET); + //String jsonStr = JSON.toJSONString(map); + while (Util.isNotEmpty(token)) { + //* 2. 使用生成的 client_id 和 client_secret 请求获取token接口 http://localhost:5700/open/auth/token?client_id=xxxxxx&client_secret=xxxxxxxx + String responseStr = HttpRequest.get(GET_TOKEN + "?client_id=" + CLIENT_ID + "&client_secret=" + CLIENT_SECRET).execute().body(); + if (ObjectUtil.isNotEmpty(responseStr)) { + //{"code":200,"data":{"token":"950e3060-d714-4f6a-9839-c098a116f0a8","token_type":"Bearer","expiration":1705743778}} + JSONObject jsonObject = JSON.parseObject(responseStr); + if (Objects.equals(String.valueOf(jsonObject.getString("code")), "200")) { + JSONObject response = jsonObject.getJSONObject("data"); + redisTemplate.opsForValue().set(QL_TOKEN_KEY, (String) response.get("token"), (int) response.get("expiration"), TimeUnit.SECONDS); + token = (String) response.get("token"); + } + } + sleep(500); + } + } + return token; + } + + + // get /envs + + /** + * @return + * @throws + * @description 获取所有环境变量 + */ + public JSONArray getEnv(String searchKey) { + String token = getToken(); + JSONArray result = new JSONArray(); + int maxRetryCount = 3; + int retryCount = 0; + String getUrl = GET_ENV + "?searchValue=&t=1630032278171"; + if (Util.isNotEmpty(searchKey)) { + getUrl = GET_ENV + "?searchValue=" + searchKey + "&t=1630032278171"; + } + while (result.size() == 0 && retryCount < maxRetryCount) { + + String responseStr = HttpRequest.get(getUrl).header("Authorization", "Bearer " + token).execute().body(); + if (ObjectUtil.isNotEmpty(responseStr)) { + JSONObject qlResponse = JSONObject.parseObject(responseStr); + if (qlResponse.getString("code").equals("200")) { + + result = (JSONArray) qlResponse.get("data"); + //result = null; + } + } + retryCount++; + sleep(1000); + } + return result; + } + + /** + * @param name + * @return + * @throws + * @description 检查是否存在环境变量,存在返回值,不存在返回null + */ + //public String getEnvValue(String name) { + // JSONObject jsonObject = getEnv(); + // if (ObjectUtil.isNotEmpty(jsonObject)) { + // return jsonObject.getJSONObject(name).getString("value"); + // } + // return null; + //} + + // post /envs + // { + // "value": "123", + // "name": "test_add", + // "remarks": "新建测试" + //} + + /** + * @param name + * @param remarks + * @param value + * @return + * @throws + * @description 添加环境变量 name 环境变量名,value 环境变量值,remarks 环境变量备注 + * 如果存在一样的value 会返回 400 + * 如果添加成功,就返回 TRUE ,否则返回 FALSE + */ + public Boolean addEnv(String value, String name, String remarks) { + String token = getToken(); + JSONObject result = new JSONObject(); + List bodyArr = Collections.singletonList(new QLEnv(value, name, remarks)); + logger.info("addEnv 请求body {}", JSON.toJSONString(bodyArr)); + + // 设置最大重试次数 + int maxRetryCount = 3; + int retryCount = 0; + + while (Util.isEmpty(result) && retryCount < maxRetryCount) { + String responseStr = HttpRequest.post(GET_ENV) + .header("Authorization", "Bearer " + token) + .body(JSON.toJSONString(bodyArr)) + .execute() + .body(); + logger.info("responseStr 响应 {}", responseStr); + + if (Util.isNotEmpty(responseStr)) { + JSONObject qlResponse = JSONObject.parseObject(responseStr); + String code = qlResponse.getString("code"); + + if ("200".equals(code)) { + logger.info("200 响应 {}", qlResponse); + result = (JSONObject) qlResponse.get("data"); + return true; + } else if ("400".equals(code)) { + logger.info("400 响应 {}", qlResponse); + result = (JSONObject) qlResponse.get("message"); + break; + } + } + + retryCount++; + sleep(1000); + } + return false; + + } + + // http://134.175.126.60:45700/api/crons?searchValue=&page=1&size=20&filters={}&queryString={%22filters%22:null,%22sorts%22:null,%22filterRelation%22:%22and%22}&t=1703148561868 + + /** + * @param searchKey + * @return + * @throws + * @description 获取定时任务列表 + */ + public List getCron(String searchKey) { + String token = getToken(); + List cronList = new ArrayList<>(); + int maxRetryCount = 3; + int retryCount = 0; + + String getUrl = GET_CRON + "?searchValue=&t=1630032278171"; + if (Util.isNotEmpty(searchKey)) { + getUrl = GET_CRON + "?searchValue=" + searchKey + "&t=1630032278171"; + } + while (cronList.size() == 0 && retryCount < maxRetryCount) { + String responseStr = HttpRequest.get(getUrl).header("Authorization", "Bearer " + token).execute().body(); + if (ObjectUtil.isNotEmpty(responseStr)) { + JSONObject qlResponse = JSONObject.parseObject(responseStr); + String code = qlResponse.getString("code"); + //{"code":200,"data":{"data":[],"total":0}} + if ("200".equals(code)) { + logger.info("200 响应 {}", qlResponse); + JSONObject dataObj = qlResponse.getJSONObject("data"); + if (dataObj.getInteger("total") == 0) { + + + } else { + + ArrayList data = JSONObject.parseObject(dataObj.getString("data"), ArrayList.class); + System.out.println(data.size()); + + } + } else if ("400".equals(code)) { + logger.info("400 响应 {}", qlResponse); + + //result = (JSONArray) qlResponse.get("message"); + } + retryCount++; + sleep(500); + } + } + return null; + } + + /* + * 请求网址: + * http://134.175.126.60:45700/api/crons/run?t=1703148561868 + * 请求方法: + * PUT*/ + //public JSONObject runCron(String cronId) { + // String token = getToken(); + // if (StrUtil.isNotEmpty(token)) { + // for (int i = 0; i < 3; i++) { + // String responseStr = HttpRequest.put(GET_CRON + "?cronId=" + cronId).header("Authorization", "Bearer " + token).execute().body(); + // if (ObjectUtil.isNotEmpty(responseStr)) { + // QLResponse qlResponse = JSON.parseObject(responseStr, QLResponse.class); + // if (Objects.equals(String.valueOf(qlResponse.getCode()), "200")) { + // return qlResponse.getData(); + // } + // } + // sleep(500); + // } + // } + // return null; + //} + + /** + * } + * async ["getLoginedUserInfo"](_0x4ef1fe = {}) { + * let _0x26ac1b = false; + * try { + * const _0x3daa7c = { + * "token": this.token + * }; + * const _0xe8bb64 = { + * "fn": "getLoginedUserInfo", + * "method": "get", + * "url": "https://i.meituan.com/wrapapi/getLoginedUserInfo", + * "searchParams": _0x3daa7c + * }; + * let { + * result: _0x48e5d7 + * } = await this.request(_0xe8bb64); + * if (_0x48e5d7?.["mobile"]) { + * _0x26ac1b = true; + * this.name = _0x48e5d7.nickName; + * this.userId = Number(_0x48e5d7.userId); + * this.log("登录成功"); + * } else { + * this.log("获取账号信息失败, ck可能失效");//, {"notify": true} + * await expireNotify(this.userId, this.index); + * } + * } catch (_0x232c57) { + * console.log(_0x232c57); + * } finally { + * return _0x26ac1b; + * } + * } + */ + + // 通过token 获取用户信息 + // url https://i.meituan.com/wrapapi/getLoginedUserInfo + // 参数 token + // get + public HashMap getLoginedUserInfo(String token) { + String responseStr = HttpRequest.get("https://i.meituan.com/wrapapi/getLoginedUserInfo" + "?token=" + token).header("token", token).execute().body(); + HashMap result = new HashMap<>(); + if (ObjectUtil.isNotEmpty(responseStr)) { + JSONObject qlResponse = JSON.parseObject(responseStr); + //{"userId":"3822095266","nickName":"XLP200660795","growthValue":null,"growthLevel":null,"avatarUrl":"","mobile":"15817969021"} + if (qlResponse.containsKey("mobile")) { + result.put("mobile", qlResponse.getString("mobile")); + } + if (qlResponse.containsKey("nickName")) { + result.put("nickName", qlResponse.getString("nickName")); + } + } + return result; + } + + // new QLEnv(value, name, remarks) + @Data + @AllArgsConstructor + @NoArgsConstructor + private static class QLEnv { + private String value; + private String name; + private String remarks; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + private class Cron { + private long id; + private String name; + private String command; + private String schedule; + private String timestamp; + private boolean saved; + private long status; + private long isSystem; + private long pid; + private long isDisabled; + private long isPinned; + private String logPath; + private List labels; + private long lastRunningTime; + private long lastExecutionTime; + private long subID; + private String createdAt; + private String updatedAt; + } + +} diff --git a/src/main/java/cn/van/business/util/Util.java b/src/main/java/cn/van/business/util/Util.java new file mode 100644 index 0000000..a9f7976 --- /dev/null +++ b/src/main/java/cn/van/business/util/Util.java @@ -0,0 +1,1547 @@ +package cn.van.business.util; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/21 0021 上午 11:27 + * @description: 旗云的 Util 用习惯了 用一下 + */ + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.util.HtmlUtils; + +import java.io.*; +import java.math.BigDecimal; +import java.util.*; + +/** + * 最基础的工具类 + */ +public class +Util { + + private static final Logger log = LoggerFactory.getLogger(Util.class); + + /** + * byte数组倒序 + * + * @param bytes + * @return + */ + public static final byte[] byteArrayDesc(byte[] bytes) { + if (Util.isEmpty(bytes)) { + return new byte[0]; + } + int len = bytes.length; + byte[] tmp = new byte[len]; + for (int i = len - 1; i >= 0; i--) { + tmp[len - i - 1] = bytes[i]; + } + return tmp; + + } + + /** + * 打印程序耗时 + * + * @param msg + */ + public static final void consume(long start, long end, String msg) { + System.err.println(String.format("%s consume times: %s=%s-%s", msg, (end - start), end, start)); + } + + /** + * 将非法且不可见的XML字符转为空格(空格的的ascii码是32) + * + * @param bts byte[] + * @return 转换后的 byte[] + */ + public static final byte[] convertInvalidInvisibleToSpace(byte[] bytes) { + if (Util.isEmpty(bytes)) { + return new byte[0]; + } + byte[] tmp = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + if (b >= 0 && b <= 8 || b >= 14 && b <= 31 || b == 11 || b == 12 || b == 127) { + tmp[i] = 32; + } else { + tmp[i] = bytes[i]; + } + } + return tmp; + } + + /** + * 将非法且不可见的XML字符转为空格 + */ + public static final String convertInvalidInvisibleToSpace(String str) { + if (Util.isEmpty(str)) { + return ""; + } + return new String(Util.convertInvalidInvisibleToSpace(str.getBytes())); + } + + /** + * 将非法且不可见的XML字符转为空格 + * + * @param bts byte[] + * @return 转换后的 byte[] + */ + public static final byte[] convertInvalidInvisibleXml(byte[] bytes) { + if (Util.isEmpty(bytes)) { + return new byte[0]; + } + byte[] tmp = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + if (b >= 0 && b <= 8)// \\x00-\\x08 + { + tmp[i] = 32; + } else if (b >= 14 && b <= 31)// \\x0e-\\x1f + { + tmp[i] = 32; + } else if (b == 11 || b == 12 || b == 127)// \\x0b-\\x0c and \\x7f + { + tmp[i] = 32; + } else { + tmp[i] = bytes[i]; + } + } + return tmp; + } + + /** + * 删除临时文件. + * + * @param dir + */ + public static final void deleteTempFile(String dir) { + File tf = new File(dir); + String[] childs = tf.list(); + for (String child : childs) { + new File(dir + File.separator + child).delete(); + } + } + + /** + * 读取文件路径,返回DataSource容器 + * + * @param attFiles + * @return + */ + //public static final List fileDirsToDatasource(List attFiles) { + // DataSource dc = null; + // List dcs = new ArrayList(); + // if (isNotEmpty(attFiles)) { + // for (File file : attFiles) { + // dc = new FileDataSource(file); + // dcs.add(dc); + // } + // } + // return dcs; + //} + + /** + * 得到数组中非空元素的数量 + * + * @param arr + * @return + */ + public static final int getArraySizeWithoutEmptyElement(String[] arr) { + if (Util.isEmpty(arr)) { + return 0; + } + int count = 0; + for (String s : arr) { + if (!Util.isEmpty(s)) { + count++; + } + } + return count; + } + + /** + * 指定cookie的key,获取对应的value。 + * + * @param request + * @param key cookie key + * @return cookie value + */ + //public static final String getCookieValue(HttpServletRequest request, String key) { + // if (Util.isAnyEmpty(key, request)) { + // return ""; + // } + // Cookie[] cookies = request.getCookies(); + // if (Util.isNotEmpty(cookies)) { + // for (Cookie c : cookies) { + // if (key.equals(c.getName())) { + // if (Util.isNotEmpty(c.getValue())) { + // return c.getValue(); + // } + // break; + // } + // } + // } + // return ""; + //} + + /** + * @param response + * @param key 键 + * @param value 值 + * @param maxAge 存活时间,默认-1 + * @param path 有效域,默认/ + */ + //public static void setCookie(HttpServletResponse response, String key, String value, Integer maxAge, String path) { + // Cookie cookie = new Cookie(key, value); + // + // // 设置浏览器的cookie保留时间,单位秒。负值标识浏览器关闭即删除cookie,0表示马上删除。 + // // 这里设置成一年,如果想控制登录超时,可以用ADMIN_SESSION_TIMEOUT在t_parameter中设置redis的超时时间,统一后台控制。 + // if (Util.isEmpty(maxAge)) { + // cookie.setMaxAge(-1); + // } else { + // cookie.setMaxAge(maxAge); + // } + // + // if (Util.isEmpty(path)) { + // cookie.setPath("/"); + // } else { + // cookie.setPath(path); + // } + // + // // 这两个参数是因为应付漏洞扫描加入的。 + // // 不允许前端javaScript显性读取 + // // cookie.setHttpOnly(true); + // // 只允许https请求时提交给服务器,也就是说http(测试环境)服务器端读取不到cookie。 + // // cookie.setSecure(true); + // + // response.addCookie(cookie); + //} + + /** + * 将InputStream流写入一个File对象中返回 + * + * @param inputStream + * @return + * @throws IOException + */ + //public static final File inputStreamToFile(InputStream in, String name) throws IOException { + // if (Util.isEmpty(in)) { + // return null; + // } + // if (Util.isEmpty(name)) { + // name = Util.times() + ""; + // } + // File f = new File(name); + // FileUtils.copyInputStreamToFile(in, f); + // return f; + //} + + /** + * 判断送入的字符串是否全部为中文 + * + * @param name 需判断的字符串 + * @return 判断结果返回 true为中文 false为非中文 + */ + public static final boolean isChinese(String str) { + if (Util.isEmpty(str)) { + return false; + } + str = str.replaceAll("[\u4e00-\u9FFF]", ""); + if (Util.isEmpty(str)) { + return true; + } + return false; + } + + /** + * 判断一个对象是否为空或者长度为0时返回true,否则返回false String:null或length=0返回true + * List,Set,Map:null或size=0返回true 其他null返回true + * + * @param o + * @return + */ + @SuppressWarnings("rawtypes") + public static final boolean isEmpty(Object o) { + if (null == o) { + return true; + } + if (o instanceof String) { + return (0 == ((String) o).trim().length()); + } + if (o instanceof List) { + return (0 == ((List) o).size()); + } + if (o instanceof Set) { + return (0 == ((Set) o).size()); + } + if (o instanceof Map) { + return (0 == ((Map) o).size()); + } + if (o instanceof Object[]) { + return (0 == ((Object[]) o).length); + } + return false; + } + + /** + * 判断一个对象不为null,并且长度不为0时返回true,否则返回false 规则参考Util.isEmpty() + * + * @param o + * @return + * @see #isEmpty(Object o) + */ + public static final boolean isNotEmpty(Object o) { + return !isEmpty(o); + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final String getNotNull(String t) { + return isEmpty(t) ? new String("") : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Character getNotNull(Character t) { + return isEmpty(t) ? new Character('\0') : t; + } + + /** + * 获取非空对象 + * + * @param t + * @return + */ + public static final Byte getNotNull(Byte t) { + byte b = 0; + return isEmpty(t) ? b : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Short getNotNull(Short t) { + short s = 0; + return isEmpty(t) ? s : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Integer getNotNull(Integer t) { + return isEmpty(t) ? new Integer(0) : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Long getNotNull(Long t) { + return isEmpty(t) ? new Long(0) : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Float getNotNull(Float t) { + return isEmpty(t) ? new Float(0) : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static final Double getNotNull(Double t) { + return isEmpty(t) ? new Double(0) : t; + } + + /** + * 获取非空对象,若空返回对象类型默认值 + * + * @param t + * @return + */ + public static BigDecimal getNotNull(BigDecimal t) { + return isEmpty(t) ? new BigDecimal(0) : t; + } + + /** + * 倒序行读文件内容(确保文件为UTF-8编码,才能显示中文)。 + * + * @param filename 文件完整路径(确保文件必须存在并且是文件而不是路径) + * @param seekIndex 从该字节位置往前读取,初始为0,输入null时从最后一个字节往前读取 + * @param howManyLineToRead 读取行数 + * @param isNewline 返回的内容是否换行 + * @return 返回指针位置(下标0 , 用于下次继续读取 ) _读取内容 ( 下标1) + * @throws BusinessException + */ + //public static final List readLineDesc(String filename, Long seekIndex, int howManyLineToRead, + // Boolean isNewline) throws BusinessException { + // + // if (Util.isEmpty(filename)) { + // return null; + // } + // + // if (seekIndex != null && seekIndex <= 0 || howManyLineToRead <= 0) { + // return null; + // } + // + // RandomAccessFile rf = null; + // StringBuffer result = new StringBuffer(""); + // List temResult = new ArrayList(); + // int lineCount = 0; + // List seekIndex_resultStr = null; + // String r = ""; + // if (isNewline) { + // r = "\n"; + // } + // + // try { + // rf = new RandomAccessFile(filename, "r"); + // // 计算文件指针位置,默认是文件字节数-1,即从最后一个字节开始 + // long nextend = 0L; + // if (seekIndex == null) { + // // 得到文件最后一个字节的指针,指向该字节下方 + // nextend = rf.length() - 1; + // } else { + // nextend = seekIndex; + // } + // + // int c = -1; + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // while (nextend >= 0 && howManyLineToRead > lineCount) { + // rf.seek(nextend); + // // 读取指针指向的字节 + // c = rf.read(); + // // 如果该字节的ascii码与换行符的ascii码相等 + // if ((c == '\n') && nextend != 0) { + // lineCount++; + // String line = Util.trim(new String(byteArrayDesc(out.toByteArray()))); + // if (Util.isNotEmpty(line)) { + // temResult.add(line + r); + // } + // out = new ByteArrayOutputStream(); + // } else { + // out.write(c); + // } + // if (nextend == 0) {// 当文件指针退至文件开始处,输出第一行 + // String line = Util.trim(new String(byteArrayDesc(out.toByteArray()))); + // if (Util.isNotEmpty(line)) { + // temResult.add(line + r); + // } + // log.info("Have seek to the first byte."); + // break; + // } + // nextend--; + // } + // seekIndex_resultStr = new ArrayList(); + // seekIndex_resultStr.add(nextend + ""); + // for (int i = temResult.size() - 1; i >= 0; i--) { + // if (i == 0) { + // result.append(temResult.get(i).trim()); + // } else { + // result.append(temResult.get(i)); + // } + // } + // seekIndex_resultStr.add(result.toString()); + // return seekIndex_resultStr; + // } catch (FileNotFoundException e) { + // throw new BusinessException("Can't locate file " + filename + ",", e); + // } catch (IOException e) { + // throw new BusinessException("Read data from file " + filename + " failed,", e); + // } finally { + // try { + // if (rf != null) { + // rf.close(); + // } + // } catch (IOException e) { + // throw new BusinessException("Close file " + filename + " failed,", e); + // } + // } + //} + + /** + * 获取当前时间毫秒数 + * + * @return + */ + public static final long times() { + return System.currentTimeMillis(); + } + + /** + * 强化trim() + * + * @param val + * @return + */ + public static final String trim(String val) { + if (isEmpty(val)) { + return ""; + } else { + return val.trim(); + } + } + + /** + * 将File f对象写到服务器本地 + * + * @param f + * @param dir 服务器本地路径,可以是一个目录路径,也可以是一个文件路径 如果是目录路径,此目录若不存在,程序自动创建 + * 如果是文件路径,程序会自动创建此文件,包括不存在的目录,并将File + * f写入此文件中,注意,文件路径必须有文件后缀,否则被认为是目录路径 如果是目录路径,文件名是f.getName(); + * 如果是文件路径,文件名为路径中指定的文件名; + * 路径如果不存在,创建的时候,如果此路径为绝对路径,那么创建相应的绝对路径,如果是相对路径,那么相对于用户的user目录,参考File + * getAbsolutePath()方法 + * @throws IOException + * @throws BusinessException + */ + //public static final void writeFileToLocalDir(File f, String dir) throws IOException, BusinessException { + // if (Util.isEmpty(f)) { + // throw new BusinessException("Util.writeFileToLocalDir()'s File f is null."); + // } + // if (Util.isEmpty(dir)) { + // throw new BusinessException("Util.writeFileToLocalDir()'s File dir is empty."); + // } + // File dirf = new File(dir); + // if (!dirf.exists()) { + // String[] dirArr = dirf.getAbsolutePath().split("\\" + File.separator); + // String lastDir = dirArr[dirArr.length - 1]; + // if (!lastDir.contains(".")) { + // dirf = new File(dirf.getAbsolutePath() + File.separator + f.getName()); + // } + // } else if (dirf.exists() && dirf.isDirectory()) { + // dirf = new File(dirf.getAbsolutePath() + File.separator + f.getName()); + // } + // FileUtils.writeByteArrayToFile(dirf, FileUtils.readFileToByteArray(f)); + //} + + /** + * 将字符串的首字母变小写 + * + * @param s + * @return + */ + public static final String lowerCaseFirstChar(String s) { + if (Util.isEmpty(s)) { + return ""; + } + return s.substring(0, 1).toString().toLowerCase() + s.substring(1).toString(); + } + + /** + * 将字符串的首字母变大写 + * + * @param s + * @return + */ + public static final String upperCaseFirstChar(String s) { + if (Util.isEmpty(s)) { + return ""; + } + return s.substring(0, 1).toString().toUpperCase() + s.substring(1).toString(); + } + + /** + * 只要其中一个对象是empty,返回true,否则返回false + * + * @param o + * @return + */ + public static final boolean isAnyEmpty(Object... o) { + for (int i = 0; i < o.length; i++) { + Object obj = o[i]; + if (Util.isEmpty(obj)) { + return true; + } + } + return false; + } + + /** + * 所有对象都不为empty,返回true,否则返回false + * + * @param o + * @return + */ + public static final boolean isNotAnyEmpty(Object... o) { + return !isAnyEmpty(o); + } + + /** + * 获取UUID,纳秒+uuid,用纳秒开头是为了让记录可以使用这个uuid进行排序 + */ + public static final String getUUID() { + return System.nanoTime() + UUID.randomUUID().toString().replaceAll("\\-", ""); + } + + /** + * 使用不可见字符串Constant.unsee0x01将字符串隔开,重新组成一串字符串 + * + * @param strs + * @return + */ + public static final String joinStringWithUnseeChar(Collection strs) { + return joinStringWithSplitor(strs, String.valueOf((char) 0x01)); + } + + /** + * 用任何分隔符将字符串容器中的字符串组成一串字符串。 + * + * @param strs + * @param splitor + * @return + */ + public static final String joinStringWithSplitor(Collection strs, String splitor) { + if (Util.isEmpty(strs)) { + return ""; + } + StringBuffer res = new StringBuffer(""); + for (String s : strs) { + res.append(s + splitor); + } + if (Util.isNotEmpty(res.toString())) { + return res.toString().substring(0, res.toString().length() - 1); + } + return res.toString(); + } + + /** + * 使用不可见字符串Constant.unsee0x01将字符串隔开,重新组成一串字符串 + * + * @param strs + * @return + */ + public static final String joinStringWithUnseeChar(String[] strs) { + return joinStringWithSplitor(strs, String.valueOf((char) 0x01)); + } + + /** + * 用任何分隔符将字符串数组中的字符串组成一串字符串。 + * + * @param strs + * @param splitor + * @return + */ + public static final String joinStringWithSplitor(String[] strs, String splitor) { + if (Util.isEmpty(strs)) { + return ""; + } + StringBuffer res = new StringBuffer(""); + for (String s : strs) { + res.append(s + splitor); + } + if (Util.isNotEmpty(res.toString())) { + return res.toString().substring(0, res.toString().length() - 1); + } + return res.toString(); + } + + /** + * 去除字符串数组中的空串 + * + * @param split + * @return + */ + public static final String[] removeEmptyElements(String[] split) { + List l = new ArrayList(); + for (String s : split) { + if (Util.isNotEmpty(s)) { + l.add(s); + } + } + String[] res = new String[l.size()]; + return l.toArray(res); + } + + /** + * 去除队列中为null的元素 + * + * @param list + * @return + */ + @SuppressWarnings("unchecked") + public static List removeNull(List list) { + if (Util.isEmpty(list)) { + return (List) list; + } + list.removeAll(Collections.singleton(null)); + List res = (List) list; + return res; + } + + /** + * 去除队列中为null的元素 + * + * @param list + * @return + */ + @SuppressWarnings("unchecked") + public static List removeEmpty(List list) { + if (Util.isEmpty(list)) { + return (List) list; + } + List newList = new ArrayList(); + for (T t : list) { + if (Util.isNotEmpty(t)) { + newList.add(t); + } + } + return newList; + } + + /** + * 获取文件后缀 + * + * @param fileName 文件名,File对象调用getName(); + * @return + */ + public static final String getSuffix(String fileName) { + if (Util.isEmpty(fileName)) { + return ""; + } + fileName = getRealFileName(fileName); + return fileName.substring(fileName.lastIndexOf(".")); + } + + /** + * 获取文件后缀(连后缀的最开始的“.”都不要了) + * + * @param fileName 文件名,File对象调用getName(); + * @return + */ + public static final String getSuffixWithoutDot(String fileName) { + if (Util.isEmpty(fileName)) { + return ""; + } + fileName = getRealFileName(fileName); + return fileName.substring(fileName.lastIndexOf(".") + 1); + } + + /** + * 获取除掉后缀的文件名 + * + * @param fileName 文件名,File对象调用getName(); + * @return + */ + public static final String getFileNameIgnoreSuffix(String fileName) { + if (Util.isEmpty(fileName)) { + return ""; + } + fileName = getRealFileName(fileName); + return fileName.substring(0, fileName.lastIndexOf(".")); + } + + /** + * 获取文件名,有些文件名是一串路径,有些是正确的文件名,这个方法做兼容。 + * 比如IE上传到后台接收到的MultipartFile的getOriginalFilename就是一串路径。 + * + * @return 最终的文件名 + */ + public static final String getRealFileName(String fileName) { + if (fileName.indexOf("/") != -1) { + fileName = fileName.substring(fileName.lastIndexOf("/") + 1); + } + if (fileName.indexOf("\\") != -1) { + fileName = fileName.substring(fileName.lastIndexOf("\\") + 1); + } + return fileName; + } + + /** + * 删除第一个字符以及最后一个字符 + * + * @return + */ + public static final String removeFirstLastChar(String s) { + if (Util.isEmpty(s)) { + return ""; + } + return s.substring(1, s.length() - 1); + } + + /** + * spring mvc专用下载 + * + * @param response + * @param request + * @param name 客户端附件下载的名字 + * @param b 下载数据 + * @throws IOException + * @throws BusinessException + */ + //public static final ResponseEntity write(String name, byte[] b) throws IOException, BusinessException { + // try { + // HttpHeaders headers = new HttpHeaders(); + // headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + // String downloadFileName = new String(name.getBytes(Constant.ENCODING), "iso-8859-1");// 设置编码 + // headers.setContentDispositionFormData("attachment", downloadFileName); + // return new ResponseEntity(b, headers, HttpStatus.CREATED); + // } catch (Exception e) { + // throw new BusinessException(e); + // } + //} + + /** + * 将Map的value中的String[]变成以Constant.unsee0x01隔开的字符串。 原因是Controller不能传递Map的形式到WS层方法, 因为不想做那么复杂,当前只支持用j_m_m_xxx传递Map + * 的变量,所以当分页的时候,需要传递的searchParams就是Map + * 形式的,所以需要将Map里面的String[]变成字符串,从而整个变量就能使用j_map_xxx来传递 + * + * @param j_map_searchParams + * @return + */ + //public static Map convertMapArrayToStirng(Map j_map_searchParams) { + // Map res = new HashMap(); + // if (Util.isEmpty(j_map_searchParams)) { + // return res; + // } + // for (Entry en : j_map_searchParams.entrySet()) { + // if (Util.isNotEmpty(en.getValue())) { + // if (en.getValue() instanceof String[]) { + // res.put(en.getKey(), Util.joinStringWithUnseeChar((String[]) en.getValue())); + // } else { + // res.put(en.getKey(), (String) en.getValue()); + // } + // } + // } + // return res; + //} + + /** + * 格式化文件的大小,如果小于1024kb,直接显示kb,如果大于1024kb,换算成mb进行显示,以此类推。 + * + * @param size 传一个b为单位的数字 + * @return + */ + public static String formatFileSize(long size) { + String fileSize = ""; + double kbNum = new BigDecimal(size).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP).doubleValue(); + if (1024 < kbNum) { + double mbNum = new BigDecimal(kbNum).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP) + .doubleValue(); + if (1024 < mbNum) { + double gbNum = new BigDecimal(mbNum).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP) + .doubleValue(); + fileSize = gbNum + "GB"; + } else { + fileSize = mbNum + "MB"; + } + } else { + fileSize = kbNum + "KB"; + } + return fileSize; + } + + /** + * 判断字符串中字母的个数 + */ + public static int getAlphaCharLength(String str) { + int count = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) * 1 >= 65 && str.charAt(i) * 1 <= 90 + || str.charAt(i) * 1 >= 97 && str.charAt(i) * 1 <= 122) { + count++; + } + } + return count; + } + + /** + * 判断字符串中数字个数 + */ + public static int getNumLength(String str) { + int count = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) * 1 >= 48 && str.charAt(i) * 1 <= 57) { + count++; + } + } + return count; + } + + ; + + /** + * 判断字符串中特殊字符个数。 + */ + public static int getSpecialCharLength(String str) { + int count = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) * 1 < 48 || str.charAt(i) * 1 > 57 && str.charAt(i) * 1 < 65 + || str.charAt(i) * 1 > 90 && str.charAt(i) * 1 < 97 || str.charAt(i) * 1 > 122) { + count++; + } + } + return count; + } + + ; + + /** + * 判断字符串是否包含大小写。 + */ + public static boolean containBigAndSmall(String str) { + boolean big = false; + boolean small = false; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) * 1 >= 65 && str.charAt(i) * 1 <= 90) { + big = true; + break; + } + } + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) * 1 >= 97 && str.charAt(i) * 1 <= 122) { + small = true; + break; + } + } + if (big && small) { + return true; + } + return false; + } + + /** + * 简单混淆字符串中字符。 当前是0位和1位调换,最后两位字符调换。 + * + * @param str + * @return + */ + public static String confusionStr(String str) { + char[] c = str.toCharArray(); + char a = c[1]; + c[1] = c[0]; + c[0] = a; + a = c[c.length - 1]; + c[c.length - 1] = c[c.length - 2]; + c[c.length - 2] = a; + return new String(c); + } + + /** + * 获取某个范围的随机数。 如获取0-5的随机数,minRang:0,maxRang:5, 如获取5-50的随机数,minRang:5,maxRang:50 + * + * @param minRang + * @param maxRang + * @return + */ + public static int getRandomIntInRang(int minRang, int maxRang) { + Random rand = new Random(); + int randNum = rand.nextInt(maxRang + 1) + minRang; + return randNum; + } + + /** + * 随机获取a-z,0-9中的n个字符作为验证码。 + * + * @return + */ + public static String getVerifyCodeChars(int n) { + char[] c = new char[]{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + int randNum = -1; + char[] resc = new char[n]; + for (int i = 0; i < n; i++) { + randNum = getRandomIntInRang(0, 35); + try { + resc[i] = c[randNum]; + } catch (Exception e) { + resc[i] = 'p'; + continue; + } + } + return new String(resc); + } + + /** + * 随机获取0-9中的n个字符作为验证码。一般用作短信验证码时。 + * + * @return + */ + public static String getVerifyCodeCharsOnliNum(int n) { + char[] c = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + int randNum = -1; + char[] resc = new char[n]; + for (int i = 0; i < n; i++) { + randNum = getRandomIntInRang(0, 9); + try { + resc[i] = c[randNum]; + } catch (Exception e) { + resc[i] = '8'; + continue; + } + } + return new String(resc); + } + + /** + * 去除空元素后再join + * 和javaScript的数组的join方法作用一样,主要用于将数组的元素用指定的连接字符相连成一行字符串, + * 放在searchParams中的IN参数中进行查询,如["a", "b", 1]→"a,b,1" + * + * @param array + * @param joinStr 链接字符串,可以是单个字符,也可以是多个字符的字符串,也可以是空串。 + * @return + */ + public static String joinIgnoreEmpty(List array, String joinStr) { + array = removeEmpty(array); + return join(array, joinStr); + } + + public static void main(String[] args) { + } + + /** + * 和javaScript的数组的join方法作用一样,主要用于将数组的元素用指定的连接字符相连成一行字符串, + * 放在searchParams中的IN参数中进行查询,如["a", "b", 1]→"a,b,1" + * + * @param array + * @param joinStr 链接字符串,可以是单个字符,也可以是多个字符的字符串,也可以是空串。 + * @return + */ + public static String join(List array, String joinStr) { + if (Util.isEmpty(array)) { + return ""; + } else { + StringBuffer sb = new StringBuffer(""); + for (T o : array) { + String val = ""; + if (Util.isNotEmpty(o)) { + val = o.toString(); + } + sb.append(val + joinStr); + } + if (Util.isNotEmpty(sb.toString())) { + return sb.toString().substring(0, sb.toString().length() - joinStr.length()); + } + } + return ""; + } + + /** + * 将字符串按照正则匹配分割 + */ + public static List cut(String str, String regex) { + List list = new ArrayList(); + if (Util.isEmpty(regex)) { + return list; + } + String[] ss = str.split(regex); + for (String s : ss) { + list.add(s); + } + return list; + } + + /** + * 将字符串按指定分割符切分成多段并转成List + * + * @param string + * @param splitStr + * @return + */ + public static List convertToList(String string, String splitStr) { + List list = new ArrayList(); + if (Util.isEmpty(string)) { + return list; + } + String[] strs = string.split(splitStr); + if (Util.isEmpty(strs)) { + return list; + } + for (String str : strs) { + list.add(str); + } + return list; + } + + /** + * HTML字符转义 + * + * @return String 过滤后的字符串 + * @see 对输入参数中的敏感字符进行过滤替换,防止用户利用JavaScript等方式输入恶意代码 + * @see String input = + * @see HtmlUtils.htmlEscape(input); //from spring.jar + * @see StringEscapeUtils.escapeHtml(input); //from commons-lang.jar + * @see 尽管Spring和Apache都提供了字符转义的方法,但Apache的StringEscapeUtils功能要更强大一些 + * @see StringEscapeUtils提供了对HTML,Java,JavaScript, SQL ,XML等字符的转义和反转义 + * @see 但二者在转义HTML字符时,都不会对单引号和空格进行转义,而本方法则提供了对它们的转义 + */ + public static String htmlEscape(String input) { + if (isEmpty(input)) { + return input; + } + input = input.replaceAll("&", "&"); + input = input.replaceAll("<", "<"); + input = input.replaceAll(">", ">"); + input = input.replaceAll(" ", " "); + input = input.replaceAll("'", "'"); // IE暂不支持单引号的实体名称,而支持单引号的实体编号,故单引号转义成实体编号,其它字符转义成实体名称 + input = input.replaceAll("\"", """); // 双引号也需要转义,所以加一个斜线对其进行转义 + input = input.replaceAll("\n", "
"); // 不能把\n的过滤放在前面,因为还要对<和>过滤,这样就会导致
失效了 + return input; + } + + /** + * Controller端获取searchParams + * + * @param request + * @return + */ + //public static Map getSearchParams(HttpServletRequest request) { + // Map searchParams = WebUtil.getParametersStartingWith(request, WebConstant.search); + // return searchParams; + //} + + /** + * Controller端获取searchParams (value not empty) + * + * @param request + * @return + */ + //public static Map getSearchParamsNotEmpty(HttpServletRequest request) { + // Map searchParams = getSearchParams(request); + // Map searchParamsCopy = new HashMap(searchParams); + // Set> entrys = searchParamsCopy.entrySet(); + // for (Entry entry : entrys) { + // if (Util.isEmpty(entry.getValue())) { + // searchParams.remove(entry.getKey()); + // } + // } + // return searchParams; + //} + + /** + * Controller端获取sortParams + * + * @param request + * @return + */ + //public static Map getSortParams(HttpServletRequest request) { + // Map sortParams = WebUtil.getParametersStartingWith(request, WebConstant.sort); + // return sortParams; + //} + + /** + * Controller端获取sortParams (value not empty) + * + * @param request + * @return + */ + //public static Map getSortParamsNotEmpty(HttpServletRequest request) { + // Map sortParams = getSortParams(request); + // Map sortParamsCopy = new HashMap(sortParams); + // Set> entrys = sortParamsCopy.entrySet(); + // for (Entry entry : entrys) { + // if (Util.isEmpty(entry.getValue())) { + // sortParams.remove(entry.getKey()); + // } + // } + // return sortParams; + //} + + /** + * java通用下载。如果使用spring mvc框架,请用spring mvc的专用下载。 + * 将数据写到客户端,供客户下载,比如客户端各种文件附件的下载,以及客户端图片的显示都是用这个方法输出到客户端。 + * + * @param response + * @param request + * @param name 客户端附件下载的名字 + * @param b 下载数据 + * @throws IOException + * @throws BusinessException + * @see #write(String, byte[]) + */ + //public static final void write(HttpServletRequest request, HttpServletResponse response, String name, byte[] b) + // throws IOException, BusinessException { + // OutputStream out = null; + // try { + // out = response.getOutputStream(); + // response.setContentType("APPLICATION/OCTET-STREAM"); + // response.setCharacterEncoding(Constant.ENCODING); + // response.setHeader("Pragma", "public"); + // response.setHeader("Cache-Control", "max-age=0"); + // String agent = (String) request.getHeader("USER-AGENT"); + // if (agent != null && agent.indexOf("MSIE") == -1) { + // // FF + // String enableFileName = "=?" + Constant.ENCODING + "?B?" + // + (new String(Base64.encodeBase64(name.getBytes(Constant.ENCODING)))) + "?="; + // response.setHeader("Content-Disposition", "attachment; filename=" + enableFileName); + // } else { + // // IE + // String enableFileName = new String(name.getBytes("GBK"), "ISO-8859-1"); + // response.setHeader("Content-Disposition", "attachment; filename=" + enableFileName); + // } + // out.write(b); + // } catch (Exception e) { + // throw new BusinessException(e); + // } finally { + // IOUtil.flush(out); + // IOUtil.close(out); + // } + //} + + /** + * 从字节数组中的start下标开始(包括start)下标,获取len个元素,返回一个新的数组。 + * + * @param b + * @param start + * @param len + * @return + */ + public static byte[] getTargetBytes(byte[] b, int start, int len) { + byte[] newB = new byte[len]; + int i = 0; + if (Util.isNotEmpty(b)) { + while (true) { + if (i == len) { + break; + } + newB[i] = b[start]; + i++; + start++; + } + } + return newB; + } + + /** + * 辅助方法 数组元素 + 前缀 + 分隔符 然后拼接起来 如 参数分别是 [AA,BB,CC], TDD, -- 返回结果是 + * TDDAA--TDDBB--TDDCC + * + * @param deviceIds + * @param prefix + * @return + */ + public static String join(List array, String prefix, String joinStr) { + if (array == null || prefix == null || joinStr == null) { + return ""; + } + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < array.size(); i++) { + if (i == array.size() - 1) { + buff.append(prefix + array.get(i)); + } else { + buff.append(prefix + array.get(i) + joinStr); + } + } + return buff.toString(); + } + + /** + * 拼接sql中的in参数 + * + * @param + * @param array + * @param joinStr + * @return + */ + public static String joinInStr(List array) { + if (Util.isAnyEmpty(array)) { + return ""; + } + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < array.size(); i++) { + if (Util.isNotEmpty(array.get(i))) { + if (i == array.size() - 1) { + buff.append("'" + array.get(i) + "'"); + } else { + buff.append("'" + array.get(i) + "',"); + } + } + } + return buff.toString(); + } + + /** + * 辅助方法 数组元素 + 前缀 然后返回 如 参数分别是 [AA,BB,CC], TDD 返回结果是 [TDDAA,TDDBB,TDDCC] + * + * @param deviceIds + * @param prefix + * @return + */ + @SuppressWarnings("unchecked") + public static List joinPrefix(List array, String prefix) { + if (array == null || prefix == null) { + return new ArrayList<>(); + } + List newArray = new ArrayList(array.size()); + for (T t : array) { + newArray.add((T) (prefix + t)); + } + return newArray; + } + + /** + * 从0度开始为正北方 + *

+ * Title: degrees + *

+ *

+ * Description:根据角度转化成中文风向,16位陆地 + *

+ * + * @param degrees + * @return + * @author dorry + */ + public static String degrees(Double degrees) { + if (348.75 <= degrees && degrees <= 360) { + return "北"; + } else if (0 <= degrees && degrees <= 11.25) { + return "北"; + } else if (11.25 < degrees && degrees <= 33.75) { + return "东北偏北"; + } else if (33.75 < degrees && degrees <= 56.25) { + return "东北"; + } else if (56.25 < degrees && degrees <= 78.75) { + return "东北偏东"; + } else if (78.75 < degrees && degrees <= 101.25) { + return "东"; + } else if (101.25 < degrees && degrees <= 123.75) { + return "东南偏东"; + } else if (123.75 < degrees && degrees <= 146.25) { + return "东南"; + } else if (146.25 < degrees && degrees <= 168.75) { + return "东南偏南"; + } else if (168.75 < degrees && degrees <= 191.25) { + return "南"; + } else if (191.25 < degrees && degrees <= 213.75) { + return "西南偏南"; + } else if (213.75 < degrees && degrees <= 236.25) { + return "西南"; + } else if (236.25 < degrees && degrees <= 258.75) { + return "西南偏西"; + } else if (258.75 < degrees && degrees <= 281.25) { + return "西"; + } else if (281.25 < degrees && degrees <= 303.75) { + return "西北偏西"; + } else if (303.75 < degrees && degrees <= 326.25) { + return "西北"; + } else if (326.25 < degrees && degrees < 348.75) { + return "西北偏北"; + } else { + return "无风"; + } + } + + /** + * velocity模板得到字符串,模板为绝对路径文件 + * + * @param data + * @param templateDir + * @return + */ + //public static final String getStringFromVelocityEngineByAbsuluteDir(Map data, String templateDir) { + // File templateFile = new File(templateDir); + // if (!templateFile.exists()) { + // return ""; + // } + // VelocityEngine ve = new VelocityEngine(); + // Properties p = new Properties(); + // p.put(Velocity.FILE_RESOURCE_LOADER_PATH, templateFile.getParent()); + // ve.init(p); + // Template t = ve.getTemplate(templateFile.getName(), Constant.ENCODING); + // return getStringFromVelocityEngine(data, t, templateFile); + //} + + /** + * velocity模板得到字符串,模板为相对路径文件 + * + * @param data + * @param templateDir + * @return + */ + //public static final String getStringFromVelocityEngineByRelationDir(Map data, String templateDir) { + // File templateFile = new File(templateDir); + // if (!templateFile.exists()) { + // return ""; + // } + // VelocityEngine ve = new VelocityEngine(); + // Template t = ve.getTemplate(templateFile.getPath(), Constant.ENCODING); + // return getStringFromVelocityEngine(data, t, templateFile); + //} + + /** + * velocity模板得到字符串,实现 + * + * @param data + * @param t + * @param templateFile + * @return + */ + //private static final String getStringFromVelocityEngine(Map data, Template t, File templateFile) { + // VelocityContext context = new VelocityContext(); + // if (!Util.isEmpty(data)) { + // for (Entry e : data.entrySet()) { + // context.put(e.getKey(), e.getValue()); + // } + // } + // StringWriter writer = new StringWriter(); + // t.merge(context, writer); + // log.debug(String.format("Velocity string generate for template[%s]:\n%s", templateFile.getAbsolutePath(), + // writer.toString())); + // return writer.toString(); + //} + + /** + * 读取配置文件中的值 + * 前提是确认Application.java中读取配置文件的编码是ISO8859-1 + * + * @param env + * @param key + * @return + * @throws UnsupportedEncodingException + */ + //public static String getProperty(Environment env, String key) throws UnsupportedEncodingException { + // return new String(env.getProperty(key).getBytes("ISO8859-1"), "UTF-8"); + //} + + /** + * 过滤前端参数中的javaScript脚本和html标签,防止xss攻击 + * + * @param value + * @return + */ + public static String filterDangerString(String value) { + if (Util.isEmpty(value)) { + return ""; + } + value = value.replaceAll("<", "<"); + value = value.replaceAll(">", ">"); + return value; + } + + /** + * 随机生成密钥 + * + * @return + */ + public static String getAESRandomKey(int length) { + + ArrayList strList = new ArrayList(); + + int begin = 97; + // 生成小写字母,并加入集合 + for (int i = begin; i < begin + 26; i++) { + strList.add((char) i + ""); + } + // 生成大写字母,并加入集合 + begin = 65; + for (int i = begin; i < begin + 26; i++) { + strList.add((char) i + ""); + } + // 将0-9的数字加入集合 + for (int i = 0; i < 10; i++) { + strList.add(i + ""); + } + + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int size = strList.size(); + String randomStr = strList.get(random.nextInt(size)); + sb.append(randomStr); + } + return sb.toString(); + } + + /** + * 将字符串转Integer + * + * @return + */ + public static Integer getInteger(String val) { + if (Util.isEmpty(val)) { + return 0; + } + + val = val.replaceAll("[^\\-0-9]", ""); + + if (!isNumber(val)) { + return 0; + } + + try { + return Integer.parseInt(val); + } catch (Exception e) { + return 0; + } + } + + /** + * 判断一个字符串是不是数值型字符串,包括正负小数 + * + * @param content + * @return + */ + public static boolean isNumber(String content) { + if (Util.isEmpty(content)) { + return false; + } + + String pattern = "^[\\+\\-]?[\\d]+(\\.[\\d|E]+)?$"; + return content.matches(pattern); + } + + /** + * 把输入流的内容转化成字符串 + * + * @param is + * @return + * @throws IOException + */ + public static String readInputStream(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int length = 0; + byte[] buffer = new byte[1024]; + while ((length = is.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + is.close(); + baos.close(); + return baos.toString(); + } + + /** + * hibernate返回的list map中的map是不能直接修改的,如果想修改,需要重新new一个新的list map + * + * @param result + * @return + */ + public static List> newListMap(List> result) { + List> tempList = new ArrayList>(); + if (Util.isNotEmpty(result)) { + for (Map item : result) { + tempList.add(new HashMap(item)); + } + } + return tempList; + } + +} + diff --git a/src/main/java/cn/van/business/util/WXUtil.java b/src/main/java/cn/van/business/util/WXUtil.java new file mode 100644 index 0000000..00dc470 --- /dev/null +++ b/src/main/java/cn/van/business/util/WXUtil.java @@ -0,0 +1,242 @@ +package cn.van.business.util; + +import cn.hutool.core.util.ObjectUtil; + +import cn.hutool.http.HttpRequest; +import cn.van.business.enums.WXReqType; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.HashMap; + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/22 0022 上午 09:59 + * @description: + */ +@Component +public class WXUtil { + + private static final Logger logger = LoggerFactory.getLogger(WXUtil.class); + public static final String super_admin_wxid = "wxid_ytpc72mdoskt22"; + /** + * url http://127.0.0.1:7777/DaenWxHook/httpapi/ + * 获取微信列表 (X0000) + * 微信状态检测(Q0000) + * 发送文本消息(Q0001) + * 修改下载图片 (Q0002) + * 获取个人信息 (Q0003) + * 查询对象信(Q0004) + * 获取好友列表(Q0005) + * 获取群聊列表(Q0006) + * 获取公众号列表(Q0007) + * 获取群成员列表(Q0008) + * 发送聊天记录(Q0009) + * 发送图片 (Q0010) + * 发送本地文件(Q0011) + * 发送分享链接(Q0012) + * 发送小程序(Q0013) + * 发送音乐分享(Q0014) + * 发送XML (Q0015) + * 确认收款 (Q0016) + * 同意好友请求(Q0017) + * 添加好友通过v3(Q0018) + * 添加好友_通过wxid (Q0019) + * 查询陌生人信息(Q0020) + * 邀请进群(Q0021) + * 删除好友(Q0022) + * 修改对象备注(Q0023) + * 修改群聊名称(Q0024) + * 发送名片(Q0025) + */ + public static String WX_BASE_URL; + private Environment env; + + @Autowired + public WXUtil(Environment env) { + this.env = env; + WX_BASE_URL = env.getProperty("config.WX_BASE_URL"); + System.out.println("WX_BASE_URL:" + WX_BASE_URL); + } + + // 获取微信列表 + public JSONObject getWxList() { + WxReqDate wxReqDate = createWxReqData(WXReqType.GET_WX_LIST); + String responseStr = HttpRequest.post(WX_BASE_URL).body(JSON.toJSONString(wxReqDate)).execute().body(); + if (ObjectUtil.isNotEmpty(responseStr)) { + return JSON.parseObject(responseStr); + } else { + return null; + } + + } + + // 发送文本消息 msgType 1:私聊 2:群发 + public JSONObject sendTextMessage(String wxid, String content, Integer msgType, String fromwxid) { + // 全部打印 + logger.info("发送文本消息 msgType: " + msgType + " wxid: " + wxid + " fromwxid: " + fromwxid + " content: " + content); + // 如果是自己的微信,所有信息都加上少爷 + if (wxid.equals(super_admin_wxid) || fromwxid.equals(super_admin_wxid)) { + content = "超管: 凡 !\r\n" + content; + } + //JSONObject wxList = getWxList(); + //JSONObject wxBotInfo = (JSONObject) wxList.getJSONArray("result").get(0); + //botWxid = wxBotInfo.getString("wxid"); + // + //// + //WxReqDate wxReqDate = createWxReqData(WXReqType.SEND_TEXT_MESSAGE); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type", WXReqType.SEND_TEXT_MESSAGE.getType()); + //if ((msgType.equals(1))) { + // jsonObject.put("wxid", wxid); + // content = content; + //} + ////[@,wxid=对象wxid,nick=对象昵称,isAuto=true] + //if ((msgType.equals(2))) { + // jsonObject.put("wxid", fromwxid); + // content = "[@,wxid=" + wxid + ",nick=6,isAuto=true] " + content; + //} + /* + * { + "wxid": "filehelper", + "msg": "666大佬~" + }*/ + JSONObject data = new JSONObject(); + data.put("msg", content); + data.put("wxid", wxid); + jsonObject.put("data", data); + + System.out.println(JSON.toJSONString(jsonObject)); + //wxReqDate.setData(jsonObject); + while (Util.isNotEmpty(wxid)) { + String responseStr = HttpRequest.post(WX_BASE_URL).body(JSON.toJSONString(jsonObject)).execute().body(); + if (ObjectUtil.isNotEmpty(responseStr)) { + JSONObject response = JSON.parseObject(responseStr); + //WxResponse wxResponse = JSON.parseObject(responseStr, WxResponse.class); + //System.out.println(wxResponse); + //if (Objects.equals(String.valueOf(wxResponse.getCode()), "200")) { + // return wxResponse.getData(); + //} + //JSONObject jsonObject = HttpUtil.sendPost(url, wxReqDate.getData()); + logger.info("消息响应:" + response.toString()); + return response; + } + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return null; + } + + //private JSONObject sendWxReq(WxReqDate wxReqDate) { + // if (wxReqDate == null) { + // return null; + // } else { + // logger.info("wxReqDate: {}", wxReqDate); + // + // String responseStr = HttpRequest.post(WX_BASE_URL).body(JSON.toJSONString(wxReqDate)).execute().body(); + // if (ObjectUtil.isNotEmpty(responseStr)) { + // JSONObject jsonObject = JSON.parseObject(responseStr); + // //WxResponse wxResponse = JSON.parseObject(responseStr, WxResponse.class); + // //System.out.println(wxResponse); + // //if (Objects.equals(String.valueOf(wxResponse.getCode()), "200")) { + // // return wxResponse.getData(); + // //} + // //JSONObject jsonObject = HttpUtil.sendPost(url, wxReqDate.getData()); + // return jsonObject; + // } + // } + // + // return null; + // } + //} + + /** + * { + * "type": "Q0016", + * "data": { + * "wxid": "wxid_3sx9sjgq99kd22", + * "transferid": "1000050001202207161417697440336" + * } + * } + */ + public JSONObject queRenShouKuan(String wxid, String transferid) { + String botWxid = ""; + JSONObject wxList = getWxList(); + JSONObject wxBotInfo = (JSONObject) wxList.getJSONArray("result").get(0); + botWxid = wxBotInfo.getString("wxid"); + WxReqDate wxReqDate = createWxReqData(WXReqType.CONFIRM_RECEIPT); + HashMap body = new HashMap<>(); + body.put("wxid", wxid); + body.put("transferid", transferid); + JSONObject jsonObject = new JSONObject(body); + wxReqDate.setData(jsonObject); + String responseStr = HttpRequest.post(WX_BASE_URL + "?wxid=" + botWxid).body(JSON.toJSONString(wxReqDate)).execute().body(); + logger.info("确认收款结果:responseStr: {}", responseStr); + if (ObjectUtil.isNotEmpty(responseStr)) { + return JSON.parseObject(responseStr); + } else { + return null; + } + + } + + public WxReqDate createWxReqData(WXReqType wxReqType) { + + WxReqDate wxReqDate = new WxReqDate(wxReqType.getType(), null); + + return wxReqDate; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + private class WxResponse { + /** + * { + * "code": 200, + * "msg": "操作成功", + * "result": {}, + * "wxid": "wxid_3sq4tklb6c3121", + * "port": 7305, + * "pid": 12384, + * "flag": "7777", + * "timestamp": "1657462661814" + * } + */ + private Integer code; + private String msg; + private JSONObject result; + private String wxid; + private Integer port; + private Integer pid; + private String flag; + private String timestamp; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + private class WxReqDate { + //{ + // "type": "X0000", + // "data": {} + //} + private String type; + private JSONObject data; + + } + + +} diff --git a/src/main/java/cn/van/business/util/WxMessageConsumer.java b/src/main/java/cn/van/business/util/WxMessageConsumer.java new file mode 100644 index 0000000..9d9b81e --- /dev/null +++ b/src/main/java/cn/van/business/util/WxMessageConsumer.java @@ -0,0 +1,794 @@ +package cn.van.business.util; + + +import cn.van.business.enums.FromType; +import cn.van.business.model.wx.WxMessage; +import cn.van.business.repository.SettingRepository; +import cn.van.business.repository.WxMessageDataForChatRepository; +import cn.van.business.repository.WxUserRepository; +import com.alibaba.fastjson2.JSONArray; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * @author Leo + * @version 1.0 + * @create 2023/12/19 0019 上午 11:03 + * @description: + */ +@Component +public class WxMessageConsumer { + + private static final String meituanCookie = "meituanCookie"; + /** + * key开头的为 setting 的 key + */ + private static final String key_caiDan_user = "用户菜单"; + private static final String key_caiDan_admin = "管理员菜单"; + /** + * order 开头的是接受的指令 + */ + private static final String order_caiDan = "菜单"; + private static final String order_admin = "管理员"; + /**/ + private static final Logger logger = LoggerFactory.getLogger(WxMessageConsumer.class); + + /** + * 临时参数 + * 每次扣费 + */ + private static final BigDecimal priceOfMT20 = new BigDecimal("0.2"); + private static final Integer fromGR = 10008; + private static final String SERVER_URL = "https://api.jd.com/routerjson"; + private static final String accessToken = ""; + private static final String appKey = "98e21c89ae5610240ec3f5f575f86a59"; + private static final String appSecret = "3dcb6b23a1104639ac433fd07adb6dfb"; + private final WXUtil wxUtil; + private final QLUtil qlUtil; + private final WxMessageDataForChatRepository wxMessageDataForChatRepository; + private final WxUserRepository wxUserRepository; + private final SettingRepository settingRepository; + private final JDUtils jdUtils; + + @Autowired + public WxMessageConsumer(WXUtil wxUtil, QLUtil qlUtil, + @Lazy WxMessageDataForChatRepository wxMessageDataForChatService, + @Lazy WxUserRepository wxUserRepository, + @Lazy SettingRepository settingRepository, + @Lazy JDUtils jdUtils) { + this.wxUtil = wxUtil; + this.qlUtil = qlUtil; + this.wxMessageDataForChatRepository = wxMessageDataForChatService; + this.wxUserRepository = wxUserRepository; + this.settingRepository = settingRepository; + this.jdUtils = jdUtils; + } + + /** + * 从京东商品 URL 中提取产品 ID + * + * @param url 需要解析的 URL + * @return 提取出的产品 ID + */ + public static String extractProductId(String url) { + // 使用正则表达式匹配 pattern 包含 /product/ 后跟一系列数字,结束于 .html + Pattern pattern = Pattern.compile("/product/(\\d+)\\.html"); + Matcher matcher = pattern.matcher(url); + + if (matcher.find()) { + return matcher.group(1); // 返回第一个捕获组(产品 ID) + } + return null; // 如果没有找到匹配项,返回 null + } + + //private Boolean heiMingDan(String wxid) { + // // 0 正常 1 黑名单 + // // + // boolean flag = false; + // WxUser wxUser = wxUserRepository.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // if (Util.isNotEmpty(wxUser)) { + // if (wxUser.getStatus().equals(1)) { + // flag = true; + // } + // } + // return flag; + //} + + + //private Boolean isNew(String wxid) { + // // 0 是 1 不是 + // boolean flag = false; + // WxUser wxUser = wxUserRepository.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // if (Util.isNotEmpty(wxUser)) { + // if (wxUser.getIsnew().equals(0)) { + // flag = true; + // } + // } + // return flag; + //} + + //private Boolean isNew(Integer isNew) { + // // 0 是 1 不是 + // boolean flag = false; + // if (isNew.equals(0)) { + // flag = true; + // } + // return flag; + //} + + /** + * @param wxMessage + * @return + * @throws + * @description + */ + //private void handleTransferEvent(WxMessage wxMessage) { + // Integer msgType = 1; + // + // /** + // * { + // * "fromWxid": "wxid_ytpc72mdoskt22", 对方wxid + // * "msgSource": 1, 1|收到转账 2|对方接收转账 3|发出转账 4|自己接收转账 5|对方退还 6|自己退还 + // * "transType": 1, 1|即时到账 2|延时到账 + // * "money": "2.00", 金额,单位元 + // * "memo": "", 转账备注 + // * "transferid": "1000050001202312250424037787039", 转账ID + // * "invalidtime": "1703577220" 10位时间戳 + // * }*/ + // JSONObject data = wxMessage.getData().getJSONObject("data"); + // if (data == null) { + // return; + // } + // WxMessageDataForTransfer wxMessageDataForTransfer = data.to(WxMessageDataForTransfer.class); + // + // String result = null; + // String wxid = wxMessageDataForTransfer.getFromwxid(); + // + // + // + // if (heiMingDan(wxid)) { + // result = "您已被拉黑,请联系客服!"; + // } else { + // if (wxMessageDataForTransfer.getTranstype().equals(2)) { + // result = "请勿使用延时到账功能。累计三次将永久拉黑!"; + // } else { + // + // JSONObject shouKuanResult = wxUtil.queRenShouKuan(wxid, wxMessageDataForTransfer.getTransferid()); + // if (shouKuanResult == null) { + // result = "查询转账失败,请稍后再试。"; + // } + // if (shouKuanResult != null && shouKuanResult.getInteger("code") == 200) { + // BigDecimal money = wxMessageDataForTransfer.getMoney(); + // if (money.compareTo(BigDecimal.ZERO) > 0) { + // WxUser wxUser = wxUserService.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // wxUser.setMoneyLeiji(wxUser.getMoneyLeiji().add(money)); + // wxUser.setMoneyShengyu(wxUser.getMoneyShengyu().add(money)); + // wxUser.setCountChongzhi(wxUser.getCountChongzhi().add(BigDecimal.ONE)); + // wxUserService.updateById(wxUser); + // result = "收到转账" + money + "元,已成功存入账户。感谢您的使用。"; + // } + // } + // + // } + // + // } + // wxUtil.sendTextMessage(wxid, result, msgType, null); + // + //} + private static String getUrlStr(String msg) { + //String urlPattern = "https?://[\\w-\\.]+(\\.[a-z]{2,})?(/[\\w-./?%&=]*)?" + String urlPattern = "https?://[^\\s]+?\\.(html|htm)(\\?[^\\s]*?)?"; + Pattern pattern = Pattern.compile(urlPattern); + Matcher matcher = pattern.matcher(msg); + + // 检查是否存在URL,如果存在则打印出来 + String finallyUrl = null; + if (matcher.find()) { + finallyUrl = matcher.group(); + System.out.println("Extracted URL: " + finallyUrl); + } else { + System.out.println("No URL found in the given text."); + } + if (finallyUrl != null && finallyUrl.endsWith("?")) { + // 移除最后一个字符(即问号) + finallyUrl = finallyUrl.substring(0, finallyUrl.length() - 1); + } + if (finallyUrl.contains("item.m.jd.com/product")) { + finallyUrl = finallyUrl.replace("item.m.jd.com/product", "item.jd.com"); + } + return finallyUrl; + } + + //@Async("threadPoolTaskExecutor") + public void consume(WxMessage wxMessage) throws Exception { + //logger.info("接收到消息 : {}", wxMessage); + if (wxMessage.getEvent() == null) { + return; + } + /** + * 需要处理 私聊 和 转账消息 + * 其他消息暂时不处理 + * 私聊需要解析是否美团领券 + * 转账需要对接会员系统 + * + * */ + WxMessage.DataSection data = wxMessage.getData(); + if (FromType.PRIVATE.getKey().equals(wxMessage.getEvent())) { + handlePrivateMessage(wxMessage); + } else if (FromType.GROUP.getKey().equals(wxMessage.getEvent())) { + //handleGroupMessage(wxMessage); + } + //if (event.equals(EventType.TRANSFER_EVENT.getKey())) { + // handleTransferEvent(wxMessage); + //} + + } + + /** + * 处理私聊消息 + * + * @param wxMessage + */ + private void handlePrivateMessage(WxMessage wxMessage) throws Exception { + Integer msgType = 1; + // 做业务处理 + //logger.info("处理消息: {}", JSON.toJSONString(wxMessage)); + + /** + * { + * "event": 10009, + * "wxid": "wxid_nq6r0w9v12612", + * "data": { + * "type": "recvMsg", + * "des": "收到消息", + * "data": { + * "timeStamp": "1716620300237", + * "fromType": 1, + * "msgType": 1, + * "msgSource": 0, + * "fromWxid": "wxid_3sq4tklb6c3121", + * "finalFromWxid": "", + * "atWxidList": [], + * "silence": 0, + * "membercount": 0, + * "signature": "V1_uKhKVjB1|v1_uKhKVjB1", + * "msg": " 你在干嘛呢", + * "msgId": "4937897417714063715", + * "msgBase64": "IOS9oOWcqOW5suWYm+WRog==" + * }, + * "timestamp": "1716620300238", + * "wxid": "wxid_nq6r0w9v12612", + * "port": 8888, + * "pid": 3944, + * "flag": "7888" + * } + * } + * */ + WxMessage.DataSection data = wxMessage.getData(); + WxMessage.DataSection.InnerData innerData = data.getData(); + Integer event = wxMessage.getEvent(); + + if (Util.isAnyEmpty(innerData.getMsg(), innerData.getFromWxid())) { + logger.info("消息内容为空,不处理"); + return; + } + + String msg = innerData.getMsg(); +//美团 20-7 + https://i.meituan.com/mttouch/page/account?userId=3822095266&token=AgHdIkm2tAGHc9SQSiG7M8xCx1LbTue9D2HPOAun2eYl3ou7BeEw1uGrGZH-DxmEiUgsbA1v9SM4DQAAAAC6HAAAz0rTXmkB_CIHin08hCu68mFv5k6nUc2q6_CfZqEdBcngRK_xD8Sx5fE4rfdq-yAJ, msgbase64=576O5ZuiIDIwLTcgKyBodHRwczovL2kubWVpdHVhbi5jb20vbXR0b3VjaC9wYWdlL2FjY291bnQ/dXNlcklkPTM4MjIwOTUyNjYmdG9rZW49QWdIZElrbTJ0QUdIYzlTUVNpRzdNOHhDeDFMYlR1ZTlEMkhQT0F1bjJlWWwzb3U3QmVFdzF1R3JHWkgtRHhtRWlVZ3NiQTF2OVNNNERRQUFBQUM2SEFBQXowclRYbWtCX0NJSGluMDhoQ3U2OG1GdjVrNm5VYzJxNl9DZlpxRWRCY25nUktfeEQ4U3g1ZkU0cmZkcS15QUo= + if (msg.startsWith("转链")) { + String wxid; + if (Objects.equals(event, fromGR)) { + wxid = innerData.getFromWxid(); + } else { + wxid = innerData.getFinalFromWxid(); + } + + // 使用正则表达式匹配URL + //从 转链https://item.m.jd.com/product/100065976064.html?utm_user=plusmember&gx=RnAomTM2bGbfy59DrNFzDHu0uUde7Oc&gxd=RnAoxWMLamXdwpscqIV-D94totD10SY&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=CopyURL_shareid64b2a4939719b1d3173112851071496926_shangxiang_none + // 获取 100065976064 + logger.info("处理转链消息"); + + //String finallyUrl = getUrlStr(msg); + //String finallyUrl = extractProductId(msg); + String finallyUrl = msg.substring(2); + if (Util.isNotEmpty(finallyUrl)) { + String transferResultUrl = jdUtils.transfer(finallyUrl); + wxUtil.sendTextMessage(wxid, transferResultUrl, msgType, null); + } + } else if (msg.startsWith("京粉 ")) { + jdUtils.sendOrderToWxByOrder(msg.replace("京粉 ", "")); + } + //else if (msg.startsWith("美团 ")) { + // logger.info("处理美团的消息"); + // msg = msg.substring(msg.indexOf("https://i.meituan.com/mttouch/page/account")); + // String[] all = msg.split("\\?"); + // + // if (all.length == 2) { + // String wxid = null; + // if (wxMessage.getFromtype().equals(fromGR)) { + // wxid = wxMessage.getFromid(); + // } else { + // wxid = wxMessage.getFromgid(); + // } + // String httpData = all[1]; + // String[] httpDataArr = httpData.split("&"); + // if (httpDataArr.length == 2) { + // // 调用美团 + // //String result = mt20(wxid, httpDataArr[0].split("=")[1], httpDataArr[1].split("=")[1]); + // + // //wxUtil.sendTextMessage(wxid, result, msgType, null); + // } else { + // + // wxUtil.sendTextMessage(wxid, "请检查提交的数据格式是否正确。", msgType, null); + // } + // } + //} else if ("余额".equals(msg)) { + // String wxid = null; + // if (wxMessageDataForChat.getFromtype() == 1) { + // wxid = wxMessageDataForChat.getFromwxid(); + // } else if (wxMessageDataForChat.getFromtype() == 2) { + // wxid = wxMessageDataForChat.getFinalfromwxid(); + // } + // WxUser wxUser = wxUserService.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // String result = ""; + // if (Util.isNotEmpty(wxUser)) { + // result = "您的余额为:" + wxUser.getMoneyLeiji() + "元\r"; + // result = result + " 您的消费次数为:" + wxUser.getCountXiaofei() + "次\r"; + // result = result + " 您的充值次数为:" + wxUser.getCountChongzhi() + "次\r"; + // result = result + " 您的累计充值为:" + wxUser.getMoneyLeiji() + "元"; + // } else { + // result = "暂未查询到充值记录。\r"; + // } + // + // + // wxUtil.sendTextMessage(wxid, result, msgType, null); + //} else if ("体验".equals(msg)) { + // String wxid = null; + // if (wxMessageDataForChat.getFromtype() == 1) { + // wxid = wxMessageDataForChat.getFromwxid(); + // } else if (wxMessageDataForChat.getFromtype() == 2) { + // wxid = wxMessageDataForChat.getFinalfromwxid(); + // } + // String result = ""; + // if (heiMingDan(wxid)) { + // result = "黑名单!"; + // } else { + // WxUser wxUser = wxUserService.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // if (isNew(wxUser.getIsnew())) { + // wxUser.setMoneyLeiji(wxUser.getMoneyLeiji().add(new BigDecimal(1))); + // wxUser.setMoneyShengyu(wxUser.getMoneyShengyu().add(new BigDecimal(1))); + // wxUser.setCountChongzhi(wxUser.getCountChongzhi().add(BigDecimal.ONE)); + // wxUser.setIsnew(1); + // wxUserService.updateById(wxUser); + // result = "体验成功,您已成功充值" + 1.00 + "元,已成功存入账户。感谢您的使用。"; + // } else { + // result = "您已体验过,请勿重复体验。"; + // } + // + // } + // + // wxUtil.sendTextMessage(wxid, result, msgType, null); + // + //}// 用户返回用户菜单 + //else if (order_caiDan.equals(msg)) { + // String wxid = null; + // if (wxMessageDataForChat.getFromtype() == 1) { + // wxid = wxMessageDataForChat.getFromwxid(); + // } else if (wxMessageDataForChat.getFromtype() == 2) { + // wxid = wxMessageDataForChat.getFinalfromwxid(); + // } + // String result = ""; + // String puTong = getSetting(key_caiDan_user); + // String chaoJi = getSetting(key_caiDan_admin); + // if (isSuperAdminUser(wxid)) { + // result = "用户菜单:" + puTong + " 管理员菜单:" + chaoJi; + // } else { + // result = "用户菜单:" + puTong; + // } + // + // wxUtil.sendTextMessage(wxid, result, msgType, wxMessageDataForChat.getFromwxid()); + //} + // wxMessageDataForChatService.save(wxMessageDataForChat); + } + + /** + * @param wxMessage + * @return + * @throws + * @description 处理群聊消息 + */ +// private void handleGroupMessage (WxMessage wxMessage){ +// Integer msgType = 2; +// /** +// * 接收到消息 : WxMessage(event=10009, wxid=wxid_kr145nk7l0an31, data={"type":"D0003","des":"收到消息","data":{"timeStamp":"1703128368100","fromType":1,"msgT两次ype":1,"msgSource":0,"fromWxid":"wxid_ytpc72mdoskt22","finalFromWxid":"","atWxidList":[],"silence":0,"membercount":0,"signature":"v1_vXrWK/iB","msg":"嗨鲁个迷紫123","msgBase64":"5Zeo6bKB5Liq6L+357SrMTIz"},"timestamp":"1703128368112","wxid":"wxid_kr145nk7l0an31","port":16888,"pid":10468,"flag":"7777"}) +// * 需要get 两次 data 字段*/ +// JSONObject data = wxMessage.getData().getJSONObject("data"); +// if (data == null) { +// return; +// } +// +// +// /**{"type":"D0003","des":"收到消息","data":{"timeStamp":"1702957325031","fromType":1,"msgType":1,"msgSource":0,"fromWxid":"wxid_ytpc72mdoskt22","finalFromWxid":"","atWxidList":[],"silence":0,"membercount":0,"signature":"v1_OJXJYpvM","msg":"在不","msgBase64":"5Zyo5LiN"},"timestamp":"1702957325041","wxid":"wxid_kr145nk7l0an31","port":16888,"pid":10468,"flag":"7777"} +// * */ +// WxMessageDataForChat wxMessageDataForChat = data.to(WxMessageDataForChat.class); +// +// // 做业务处理 +// logger.info("处理消息: {}", wxMessageDataForChat.toString()); +// +// /** +// * timeStamp 收到这条消息的13位现行时间戳 +// * fromType 来源类型:1|私聊 2|群聊 3|公众号 +// * msgType 消息类型:1|文本 3|图片 34|语音 42|名片 43|视频 47|动态表情 48|地理位置 49|分享链接或附件 2001|红包 2002|小程序 2003|群邀请 10000|系统消息 +// * msgSource 消息来源:0|别人发送 1|自己手机发送 +// * fromWxid fromType=1时为好友wxid,fromType=2时为群wxid,fromType=3时公众号wxid +// * finalFromWxid 仅fromType=2时有效,为群内发言人wxid +// * atWxidList 仅fromType=2,且msgSource=0时有效,为消息中艾特人wxid列表 +// * silence 仅fromType=2时有效,0 +// * membercount 仅fromType=2时有效,群成员数量 +// * signature 消息签名 +// * msg 消息内容 +// * msgBase64 消息内容的Base64 +// * */ +// if (Util.isAnyEmpty(wxMessageDataForChat.getMsg(), wxMessageDataForChat.getFromwxid(), wxMessageDataForChat.getFromtype())) { +// logger.info("消息内容为空,不处理"); +// return; +// } +// String atwxidlist = wxMessageDataForChat.getAtwxidlist(); +// if (Util.isNotEmpty((atwxidlist))) { +// JSONObject wxList = wxUtil.getWxList(); +// JSONObject wxBotInfo = (JSONObject) wxList.getJSONArray("result").get(0); +// String botWxid = wxBotInfo.getString("wxid"); +// +// if (atwxidlist.contains(botWxid)) { +// String[] split = wxMessageDataForChat.getMsg().split(" "); +// String msg; +// if (split.length == 2) { +// msg = split[1]; +// } else { +// String[] newArray = new String[split.length - 1]; +// System.arraycopy(split, 1, newArray, 0, newArray.length); +// StringBuilder stringBuilder = new StringBuilder(); +// Iterator iterator = Arrays.stream(newArray).iterator(); +// while (iterator.hasNext()) { +// String s = iterator.next(); +// stringBuilder.append(s).append(" "); +// } +// msg = stringBuilder.toString(); +// } +// +////美团 20-7 + https://i.meituan.com/mttouch/page/account?userId=3822095266&token=AgHdIkm2tAGHc9SQSiG7M8xCx1LbTue9D2HPOAun2eYl3ou7BeEw1uGrGZH-DxmEiUgsbA1v9SM4DQAAAAC6HAAAz0rTXmkB_CIHin08hCu68mFv5k6nUc2q6_CfZqEdBcngRK_xD8Sx5fE4rfdq-yAJ, msgbase64=576O5ZuiIDIwLTcgKyBodHRwczovL2kubWVpdHVhbi5jb20vbXR0b3VjaC9wYWdlL2FjY291bnQ/dXNlcklkPTM4MjIwOTUyNjYmdG9rZW49QWdIZElrbTJ0QUdIYzlTUVNpRzdNOHhDeDFMYlR1ZTlEMkhQT0F1bjJlWWwzb3U3QmVFdzF1R3JHWkgtRHhtRWlVZ3NiQTF2OVNNNERRQUFBQUM2SEFBQXowclRYbWtCX0NJSGluMDhoQ3U2OG1GdjVrNm5VYzJxNl9DZlpxRWRCY25nUktfeEQ4U3g1ZkU0cmZkcS15QUo= +// if (msg.startsWith("美团 20-7 ")) { +// logger.info("处理美团的消息"); +// msg = msg.substring(msg.indexOf("https://i.meituan.com/mttouch/page/account")); +// String[] all = msg.split("\\?"); +// +// if (all.length == 2) { +// String wxid = null; +// if (wxMessageDataForChat.getFromtype() == 1) { +// wxid = wxMessageDataForChat.getFromwxid(); +// } else if (wxMessageDataForChat.getFromtype() == 2) { +// wxid = wxMessageDataForChat.getFinalfromwxid(); +// } +// String httpData = all[1]; +// String[] httpDataArr = httpData.split("&"); +// if (httpDataArr.length == 2) { +// String result = mt20(wxMessageDataForChat.getFinalfromwxid(), httpDataArr[0].split("=")[1], httpDataArr[1].split("=")[1]); +// +// wxUtil.sendTextMessage(wxMessageDataForChat.getFinalfromwxid(), result, msgType, wxMessageDataForChat.getFromwxid()); +// } else { +// +// wxUtil.sendTextMessage(wxMessageDataForChat.getFinalfromwxid(), "请检查提交的数据格式是否正确。", msgType, wxMessageDataForChat.getFromwxid()); +// } +// +// } +// } else if ("[转账待你接收,可在手机上查看]".equals(msg)) { +// +// wxUtil.sendTextMessage(wxMessageDataForChat.getFinalfromwxid(), "暂不支持群内转账功能,请私聊进行转账充值。", msgType, wxMessageDataForChat.getFromwxid()); +// } else if ("余额".equals(msg)) { +// String wxid = null; +// if (wxMessageDataForChat.getFromtype() == 1) { +// wxid = wxMessageDataForChat.getFromwxid(); +// } else if (wxMessageDataForChat.getFromtype() == 2) { +// wxid = wxMessageDataForChat.getFinalfromwxid(); +// } +// WxUser wxUser = wxUserService.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); +// String result = ""; +// if (Util.isNotEmpty(wxUser)) { +// result = "您的余额为:" + wxUser.getMoneyLeiji() + "元\r"; +// result = result + " 您的消费次数为:" + wxUser.getCountXiaofei() + "次\r"; +// result = result + " 您的充值次数为:" + wxUser.getCountChongzhi() + "次\r"; +// result = result + " 您的累计充值为:" + wxUser.getMoneyLeiji() + "元"; +// } else { +// result = "暂未查询到充值记录。\r"; +// } +// +// +// wxUtil.sendTextMessage(wxid, result, msgType, wxMessageDataForChat.getFromwxid()); +// } else if ("体验".equals(msg)) { +// String wxid = null; +// if (wxMessageDataForChat.getFromtype() == 1) { +// wxid = wxMessageDataForChat.getFromwxid(); +// } else if (wxMessageDataForChat.getFromtype() == 2) { +// wxid = wxMessageDataForChat.getFinalfromwxid(); +// } +// String result = ""; +// if (heiMingDan(wxid)) { +// result = "黑名单!"; +// } else { +// WxUser wxUser = wxUserService.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); +// if (isNew(wxUser.getIsnew())) { +// wxUser.setMoneyLeiji(wxUser.getMoneyLeiji().add(new BigDecimal(1))); +// wxUser.setMoneyShengyu(wxUser.getMoneyShengyu().add(new BigDecimal(1))); +// wxUser.setCountChongzhi(wxUser.getCountChongzhi().add(BigDecimal.ONE)); +// wxUser.setIsnew(1); +// wxUserService.updateById(wxUser); +// result = "体验成功,您已成功充值" + 1.00 + "元,已成功存入账户。感谢您的使用。"; +// } else { +// result = "您已体验过,请勿重复体验。"; +// } +// +// } +// +// wxUtil.sendTextMessage(wxid, result, msgType, wxMessageDataForChat.getFromwxid()); +// +// } else if (msg.startsWith("S")) { +// logger.info("处理超级管理员的消息"); +// String wxid = null; +// if (wxMessageDataForChat.getFromtype() == 1) { +// wxid = wxMessageDataForChat.getFromwxid(); +// } else if (wxMessageDataForChat.getFromtype() == 2) { +// wxid = wxMessageDataForChat.getFinalfromwxid(); +// } +// if (!isSuperAdminUser(wxid)) { +// return; +// } +// +// String result = ""; +// if (heiMingDan(wxid)) { +// result = "黑名单!"; +// } else { +// String[] split1 = msg.split("\\+"); +// String superAdminOrder = split1[1]; +// if ("设置普通菜单".equals(superAdminOrder)) { +// if (split1.length == 3) { +// String value = split1[2]; +// Setting setting = settingService.getOne(new QueryWrapper().eq("setting_key", key_caiDan_admin)); +// setting.setSettingValue(value); +// settingService.saveOrUpdate(setting); +// result = "设置成功!"; +// } else { +// result = "设置失败!"; +// } +// } else if ("设置超级菜单".equals(superAdminOrder)) { +// if (split1.length == 3) { +// String value = split1[2]; +// Setting setting = settingService.getOne(new QueryWrapper().eq("setting_key", key_caiDan_admin)); +// setting.setSettingValue(value); +// settingService.saveOrUpdate(setting); +// result = "设置成功!"; +// } else { +// result = "设置失败!"; +// } +// } else if ("查询管理员".equals(superAdminOrder)) { +// result = getSetting(order_admin); +// } else if ("添加管理员".equals(superAdminOrder)) { +// if (split1.length == 3) { +// String value = split1[2]; +// Setting setting = settingService.getOne(new QueryWrapper().eq("setting_key", order_admin)); +// setting.setSettingValue(value.concat(",").concat(setting.getSettingValue())); +// settingService.saveOrUpdate(setting); +// result = "设置成功!"; +// } else { +// result = "设置失败!"; +// } +// } else if ("删除管理员".equals(superAdminOrder)) { +// if (split1.length == 3) { +// String value = split1[2]; +// Setting setting = settingService.getOne(new QueryWrapper().eq("setting_key", order_admin)); +// setting.setSettingValue(setting.getSettingValue().replace(value, "")); +// settingService.saveOrUpdate(setting); +// result = "设置成功!"; +// } else { +// result = "设置失败!"; +// } +// } +// // +// //if () +// +// wxUtil.sendTextMessage(wxid, result, msgType, wxMessageDataForChat.getFromwxid()); +// } +// } +// // 管理员返回两种菜单 +// else if (order_caiDan.equals(msg)) { +// String wxid = null; +// if (wxMessageDataForChat.getFromtype() == 1) { +// wxid = wxMessageDataForChat.getFromwxid(); +// } else if (wxMessageDataForChat.getFromtype() == 2) { +// wxid = wxMessageDataForChat.getFinalfromwxid(); +// } +// String result = ""; +// String puTong = getSetting(key_caiDan_user); +// String chaoJi = getSetting(key_caiDan_admin); +// if (isSuperAdminUser(wxid)) { +// result = "用户菜单:" + puTong + " 管理员菜单:" + chaoJi; +// } else { +// result = "用户菜单:" + puTong; +// } +// +// wxUtil.sendTextMessage(wxid, result, msgType, wxMessageDataForChat.getFromwxid()); +// } +// } +// wxMessageDataForChatService.save(wxMessageDataForChat); +// +// } +// +// } +// private String getSetting(String key) { +// if (Util.isNotEmpty(key)) { +// Setting value = settingRepository.getOne(new QueryWrapper().eq("setting_key", key)); +// return value.getSettingValue(); +// } else { +// return null; +// } +// } + + //private boolean isSuperAdminUser(String wxid) { + // boolean flag = false; + // Setting setting = settingRepository.getOne(new QueryWrapper().eq("setting_key", "管理员")); + // if (Util.isNotEmpty(setting)) { + // if (setting.getSettingValue().contains(wxid)) { + // flag = true; + // } + // } + // return flag; + //} + + /** + * @param userId + * @param token + * @return + * @throws + * @description + */ + //private String mt20(String wxid, String userId, String token) { + // /** + // * 1 查询用户余额 + // * 2 调用青龙的添加环境变量 + // * 3 执行美团领券 + // * 4 删除环境变量 + // * 5 改写返回的消息内容返回给用户 + // * */ + // logger.info("查询用户余额"); + // HashMap checkYuE = checkYuE(wxid); + // Boolean isRun = (Boolean) checkYuE.get("isRun"); + // String info = (String) checkYuE.get("info"); + // BigDecimal yuE = (BigDecimal) checkYuE.get("yuE"); + // //isRun = true; + // + // // 余额可以支持一次扣费 + // if (isRun) { + // // 调用青龙 成功 + // return runQL(token, wxid, 1); + // + // } else { + // // 调用青龙 失败 + // logger.info("余额不支持一次扣费"); + // return info; + // } + // + //} + + /** + * @param wxid + * @return + * @throws + * @description 根据 wxid 查询余额 + */ + //private HashMap checkYuE(String wxid) { + // + // HashMap result = new HashMap<>(); + // BigDecimal yuE = BigDecimal.ZERO; + // String info = ""; + // Boolean isRun = false; + // WxUser wxUser = wxUserRepository.getOne(Wrappers.query(new WxUser()).eq("wxid", wxid)); + // if (Util.isEmpty(wxUser)) { + // info = "未进行过充值,请先充值后使用。"; + // } else { + // // 如果余额小于等于零 + // if (wxUser.getMoneyShengyu().compareTo(BigDecimal.ZERO) <= 0) { + // info = "账户余额不足,请先充值后使用。"; + // } + // + // if (wxUser.getMoneyShengyu().compareTo(priceOfMT20) < 0) { + // info = "剩余余额不足以支持本次扣费,请先充值后使用。"; + // } else { + // isRun = true; + // } + // } + // + // // 返回结果 + // result.put("yuE", yuE); + // result.put("info", info); + // result.put("isRun", isRun); + // return result; + //} + + /** + * @param wxid + * @param time 调用次数,后期可以改成包月还是一次 ,目前都是 1 + * @param token + * @return + * @throws + * @description + */ + private String runQL(String token, String wxid, Integer time) { + + /** + * 1. 在系统设置 -> 应用设置 -> 添加应用,权限目前支持5个模块,可以选择多个模块。选择一个模块之后,可读写此模块的所有接口。 + * 2. 使用生成的 client_id 和 client_secret 请求获取token接口 http://localhost:5700/open/auth/token?client_id=xxxxxx&client_secret=xxxxxxxx + * 3. 上面接口返回的token有效期为30天,可用于请求青龙的接口 curl 'http://localhost:5700/open/envs?searchValue=&t=1630032278171' -H 'Authorization: Bearer + * 接口返回的token' + * 4. openapi的接口与系统正常接口的区别就是青龙里的是/api/envs,openapi是/open/envs,即就是青龙接口中的api换成open + * */ + + //String responseStr = HttpRequest.post(QL_BASE_URL + getToken()) + // .body(JSON.toJSONString(jsonMap))//头信息,多个头信息多次调用此方法即可 + // .execute().body(); + + /* + * 1.查询有没有这个环境变量 + * 2.没有就创建 + * 3.有就修改 环境变量名 需要 wxid + 手机号 + * 4.执行crons + * 5.如果是一次性的,time = 1 ,就删除环境变量(考虑改成禁用) + * 6.拿到青龙的结果 + * */ + //new QLUtil().getEnv(remark) + + + HashMap loginedUserInfo = qlUtil.getLoginedUserInfo(token); + if (Util.isNotEmpty(loginedUserInfo)) { + String mobile = loginedUserInfo.get("mobile"); + String nickName = loginedUserInfo.get("nickName"); + String remark = wxid + "+" + mobile + "+" + nickName; + JSONArray env = qlUtil.getEnv(token); + logger.info("1 查询环境变量 env = " + env); + // 第一次用 token 查询 + if (env.size() == 0) { + env = qlUtil.getEnv(wxid + "+" + mobile); + } + // 第二次用 wxid + mobile 查询,如果不存在就直接创建 + if (env.size() == 0) { + Boolean addEnv = qlUtil.addEnv(token, meituanCookie, remark); + logger.info("2 使用token查询不存在环境变量,向青龙添加变量 addEnv = " + addEnv); + } else { + // 如果存在则说明 需要更新token + logger.info("3 环境变量已存在{}", env); + } + // 这时候已经有了环境变量,可以执行crons + qlUtil.getCron("美团"); + //logger.info("查询crons cron = " + cron); + + + return "runQL 调用成功"; + + } else { + + return "获取用户信息失败"; + } + + + } + + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..666935e --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,51 @@ +server: + port: 6666 +spring: + application: + name: wxSend + #数据源配置 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.8.88:3306/jd?characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: mysql_7sjTXH + #redis配置 + redis: + host: 192.168.8.88 + port: 6379 + database: 7 + timeout: 1800000 + lettuce: + pool: + max-active: 20 + #最大阻塞等待时间(负数表示没限制) + max-wait: -1 + max-idle: 5 + min-idle: 0 + password: redis_6PZ52S # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 20MB + # 设置总上传的文件大小 + max-request-size: 20MB + #MyWebMvcConfig中开启@EnableWebMvc则失效 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + # # 对象字段为null不显示 + # default-property-inclusion: non_null + + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + +# 日志配置 +logging: + level: + cn.van: debug + org.springframework: warn +config: + WX_BASE_URL: http://192.168.8.208:7777/qianxun/httpapi?wxid=wxid_kr145nk7l0an31 + QL_BASE_URL: http://134.175.126.60:35700 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0fcf3c6..1c8e1d0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 36888 + port: 6666 spring: application: name: wxSend